【TJOI2014】匹配
题面
https://www.luogu.org/problem/P3967
题解
二分图上求可能/必然割边,由于当时还非常弱,不会用$tarjan$,所以是把边删去重跑一次的。
#include<cstdio> #include<iostream> #include<cstring> #include<vector> #include<algorithm> #include<cassert> #include<queue> #define ri register int #define N 500 #define INF 1000000007 using namespace std; int n; vector<int> id[N*5]; int w[2*N*N+4*N],tw[2*N*N+4*N],c[2*N*N+4*N],to[2*N*N+4*N],dis[N*5]; int start[2*N*N+4*N]; bool check[2*N*N+4*N]; int used[N*5]; int ans=0,cnt=-1; bool vis[N*5]; int fro[N*5],cut[N*5],tot=0,con=0; struct node { int a,b; bool operator < (const node &rhs) const { return a<rhs.a; } } sy[N*5]; void addedge(int u,int v,int co,int wo) { ++cnt; id[u].push_back(cnt); c[cnt]=co; tw[cnt]=wo; to[cnt]=v; start[cnt]=u; ++cnt; id[v].push_back(cnt); c[cnt]=-co; tw[cnt]=0; to[cnt]=u; start[cnt]=v; } queue<int> q; bool spfa() { memset(vis,0,sizeof(vis)); for (ri i=0;i<=2*n;i++) dis[i]=-INF; dis[(2*n+1)]=0; while (!q.empty()) q.pop(); q.push((2*n+1)); vis[(2*n+1)]=1; while (!q.empty()) { int x=q.front(); q.pop(); vis[x]=0; for (ri i=0;i<id[x].size();i++) { int e=id[x][i]; if (w[1^e] && dis[to[e]]<dis[x]-c[e]) { dis[to[e]]=dis[x]-c[e]; if (!vis[to[e]]) vis[to[e]]=1,q.push(to[e]); } } } return dis[0]>-INF; } int dfs(int x,int limit) { vis[x]=1; if (x==(2*n+1)||(!limit)) return limit; int get=0; for (ri i=used[x];i<id[x].size();i++) { int e=id[x][i]; if (dis[x]-c[e]==dis[to[e]] && !vis[to[e]] && w[e]) { int t=dfs(to[e],min(limit,w[e])); if (!t) continue; get+=t;limit-=t; w[e]-=t,w[1^e]+=t; used[x]=i; if (!limit) return get; } } return get; } void init() { for (ri i=0;i<=cnt;i++) w[i]=tw[i]; } void zkw(int opt) { while(spfa()) { vis[(2*n+1)]=1; while (vis[(2*n+1)]) { memset(vis,0,sizeof(vis)); memset(used,0,sizeof(used)); if (opt) ans+=dfs(0,INF)*dis[0]; else con+=dfs(0,INF)*dis[0]; } } } int main() { scanf("%d",&n); for (ri i=1;i<=n;i++) { for (ri j=1;j<=n;j++) { int co; scanf("%d",&co); addedge(i,n+j,co,1); } } for (ri i=1;i<=n;i++) addedge(0,i,0,1); for (ri i=n+1;i<=2*n;i++) addedge(i,(2*n+1),0,1); init();zkw(1); printf("%d\n",ans); int cc=0; for (ri i=0;i<=cnt;i+=2) { if (!w[i] && start[i]>=1 && start[i]<=n && to[i]>=n+1 && to[i]<=2*n) check[i]=1; } for (ri i=0;i<=cnt;i+=2) if (check[i]) { init(); w[i]=0; w[1^i]=0; con=0; zkw(0); //printf("%d\n",con); if (con!=ans) { sy[++cc]=(node){start[i],to[i]}; } } sort(sy+1,sy+cc+1); for (ri i=1;i<=cc;i++) printf("%d %d\n",sy[i].a,sy[i].b-n); }