【題解】[牛客OI周賽3-提高組]C.爆瓶子 二分圖匹配
阿新 • • 發佈:2018-12-18
學習了大佬程式碼,畢竟本蒟蒻至今寫不來二分圖匹配(NOIP退役預定qwq)
#include<cstdio> #include<cstring> const int N=500; int t,n,m,g[N][N],hd[N],tot,To[N],match[N],vis[N],lim; struct Edge{ int v,nx; }e[N*N]; inline void add(int u,int v) { e[++tot].v=v; e[tot].nx=hd[u]; hd[u]=tot; } inline int dfs(int x) { for(int i=hd[x];i;i=e[i].nx) if(!vis[e[i].v]) { vis[e[i].v]=1;//不要在外面寫vis[x]=1,x上沒有vis標記 if(!match[e[i].v]||(match[e[i].v]>lim&&dfs(match[e[i].v]))) { match[e[i].v]=x; To[x]=e[i].v; return 1; } } return 0; } inline void solve() { memset(hd,0,sizeof(hd));tot=0; for(int i=1;i<=n;i++) for(int j=n;j>=1;j--) if(g[i][j])add(i,j+n); //左邊的點編號1~n //右邊的點編號n+1~2n memset(To,0,sizeof(To)); memset(match,0,sizeof(match)); for(int i=1;i<=n;i++) { memset(vis,0,sizeof(vis)); if(!To[i])lim=0,dfs(i); } for(int i=1;i<=n;i++) { memset(vis,0,sizeof(vis)); for(int j=hd[i];j;j=e[j].nx) { if(To[i]==e[j].v)break;//沒有找到更小的解 if(match[e[j].v]<i)continue;//已與先前匹配 int x=match[e[j].v],y=To[i]; To[x]=0;match[y]=0;To[i]=e[j].v;match[e[j].v]=i; if(lim=i,dfs(x))break;//lim表示不能增廣到確定匹配的i-e[j].v To[x]=e[j].v;match[e[j].v]=x;To[i]=y;match[y]=i; } } } int main() { //freopen("in.txt","r",stdin); scanf("%d",&t); while(t--) { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) g[i][j]=1; for(int i=1;i<=m;i++) for(int j=1,x;j<=n;j++) { scanf("%d",&x); g[j][x]=0; } for(int i=1;i<=n-m;i++) { solve(); for(int j=1;j<=n;j++) printf("%d ",To[j]-n),g[j][To[j]-n]=0; puts(""); } } return 0; }
總結
二分圖匹配+字典序最小