题目
输入
输出
Sample Input
7 10
1
2
3
0 1 2
0 4 7
0 2 5
20
0 6 6
99
0 4 6
Sample Output
1
2
2
1
3
Data Constraint
对于30%的数据,N<=10^5,Q<=1000
对于100%的数据,N<=10^9,Q<=10^5
剖解题目
给一个带权二分图,求他们完美匹配的交集(即对于一条边,若每种完美匹配都参与在里面,则为答案之一)。
解法
1.zkw费用流,不会。
2.KM算法,后来学了一下,下次再详细解说。
KM求出原本的最大值,即为第一个答案。
然后,对于该答案所选的边全部记录起来,枚举每一条边,每次删去一条,再做一遍km求当前的完美匹配,如果答案比原来小,说明这条边为交集之一。
代码
#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cmath>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=85;
int a[maxn][maxn],dif[maxn],mat[maxn],exb[maxn],exg[maxn],n,ans,now;
int way[maxn][2],num,tt;
bool vb[maxn],vg[maxn];
struct cy{
int boy,girl;
}answer[maxn*maxn];
bool dfs(int boy)
{
vb[boy]=true;
fo(girl,1,n){
if (vg[girl]) continue;
int val=exb[boy]+exg[girl]-a[boy][girl];
if (!val){
vg[girl]=true;
if(mat[girl]==-1||dfs(mat[girl])){
mat[girl]=boy;
return true;
}
}
else dif[girl]=min(dif[girl],val);
}
return false;
}
void KM(int &sum)
{
memset(mat,255,sizeof(mat));
memset(exg,0,sizeof(exg));
memset(exb,0,sizeof(exb));
fo(i,1,n)
fo(j,1,n) exb[i]=max(exb[i],a[i][j]);
fo(i,1,n){
memset(dif,127,sizeof(dif));
while(1){
memset(vb,0,sizeof(vb));
memset(vg,0,sizeof(vg));
if (dfs(i)) break;
int d=1e9;
fo(j,1,n)
if (!vg[j]) d=min(d,dif[j]);
fo(j,1,n)
if(vb[j]) exb[j]-=d;
fo(j,1,n)
if(vg[j]) exg[j]+=d; else dif[j]-=d;
}
}
fo(girl,1,n) sum+=a[mat[girl]][girl];
}
bool cmp(cy a,cy b)
{
return a.boy<b.boy;
}
int main()
{
freopen("match.in","r",stdin);
freopen("match.out","w",stdout);
scanf("%d",&n);
fo(i,1,n){
fo(j,1,n) scanf("%d",&a[i][j]);
}
KM(ans);
fo(i,1,n) way[++num][0]=mat[i],way[num][1]=i;
fo(i,1,num){
int val=a[way[i][0]][way[i][1]];
a[way[i][0]][way[i][1]]=0;
now=0;
KM(now);
if(now<ans) answer[++tt].boy=way[i][0],answer[tt].girl=way[i][1];
a[way[i][0]][way[i][1]]=val;
}
sort(answer+1,answer+tt+1,cmp);
printf("%d\n",ans);
fo(i,1,tt) printf("%d %d\n",answer[i].boy,answer[i].girl);
}
要没图了。。