题解 P3967 【[TJOI2014]匹配】

Khassar

2018-03-07 15:25:05

Solution

我觉得费用流的那篇题解已经把题意说清楚了,所以我就特来贡献一下二分图最佳完美匹配的KM写法。 具体理论一些的东西你们可以去看我在$P4014$写的题解,里面说的比较多这里就不在写了。 要特别注意的是虽然我用的是$n^3$优化过的KM(我那篇题解里用的是$n^4$的,我会在代码里用注释简单说一下$n^3$优化,其实就是加了个松弛量slack),但还是在加上$n^2$枚举删边后华丽丽地TLE了3个点。 所以需要在枚举时判一下这条边有没有可能成为答案(具体看代码吧) ```cpp #include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<cstdlib> #include<string> #include<queue> #include<map> #include<vector> #include<ctime> #define ll long long #define R register #define IL inline #define Rf(a,b,c) for(R int (a)=(b);(a)<=(c);++(a)) #define Tf(a,b,c) for(R int (a)=(b);(a)>=(c);--(a)) #define MP make_pair #define PA pair<int,int> #define MES(a,b) memset((a),(b),sizeof((a))) #define MEC(a,b) memcpy((a),(b),sizeof((b))) #define D double using namespace std; const int N=105; int n,m,lx[N],ly[N],link[N],w[N][N],sl[N],sum,Ans,Link[N]; bool S[N],T[N]; IL int read() { int x=0,f=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x*=10;x+=(ch-'0');ch=getchar();} return x*f; } IL void write(int x) { if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10+'0'); } bool dfs(int x) {//二分图匹配过程 S[x]=true; Rf(i,1,n) if(lx[x]+ly[i]==w[x][i]) { if(!T[i]) { T[i]=true; if(!link[i]||dfs(link[i])) { link[i]=x; return true; } } } else {//用不能进入相等子图的边更新一下松弛量sl sl[i]=min(sl[i],lx[x]+ly[i]-w[x][i]); } return false; } IL void update() { R int a=1e9; Rf(j,1,n) if(!T[j]) //在松弛量中找,而不是再n^2枚举,优化在此 a=min(a,sl[j]); Rf(i,1,n) { if(S[i]) lx[i]-=a; if(T[i]) ly[i]+=a; sl[i]-=a; } } IL void KM() { Rf(i,1,n) { link[i]=lx[i]=ly[i]=0; Rf(j,1,n) lx[i]=max(lx[i],w[i][j]); } Rf(i,1,n) while(true) { Rf(j,1,n) { S[j]=T[j]=false;sl[j]=1e9; } if(dfs(i)) break; else update(); } } signed main() { n=read(); Rf(i,1,n) Rf(j,1,n) w[i][j]=read(); KM(); Rf(i,1,n) sum+=lx[i]+ly[i]; write(sum);putchar('\n'); Ans=sum;MEC(Link,link);//先记录一个最佳完美匹配 Rf(k,1,n) Rf(j,1,n) {//枚举删边 if(w[k][j]<w[Link[j]][j]) continue; //如果这条边比j在一个最佳完美匹配中匹配的边的边权要小,那么这条边一定不会在任意一个最佳完美匹配中,就没必要尝试删它。 R int tmp=w[k][j]; w[k][j]=-1e7; sum=0; KM(); Rf(i,1,n) sum+=lx[i]+ly[i]; w[k][j]=tmp; if(sum!=Ans) { printf("%d %d\n",k,j); } } return 0; } ``` 啦啦啦,KM跑得飞快