Description
对于100%的数据,N<=80
Analysis
这个第一问是二分图最大带权匹配
可以用KM算法(并不会)和 费用流去做
对于第二问,第一问求出任意一个最大带权匹配的时候记录一下匹配的边
枚举这些边,删掉,再跑一遍,如果答案改变就说明边在交集里
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int N=170,C=100000000,INF=2139062144;
int n,S,T,ans,a[N][N],r[N][N],c[N][N],dis[N],b[N];
bool bz[N];
void link(int u,int v,int ro,int co)
{
a[u][++a[u][0]]=v,r[u][v]=ro,c[u][v]=co;
if(ro) link(v,u,0,-co);
}
int aug(int v,int flow)
{
bz[v]=1;
if(v==T)
{
ans+=flow*dis[S];
return flow;
}
fo(i,1,a[v][0])
{
int u=a[v][i];
if(r[v][u] && !bz[u] && dis[v]==dis[u]+c[v][u])
{
int f=aug(u,min(r[v][u],flow));
if(f)
{
r[v][u]-=f,r[u][v]+=f;
return f;
}
}
}
return 0;
}
bool change()
{
int minh=INF;
fo(i,S,T)
if(bz[i])
fo(j,1,a[i][0])
{
int u=a[i][j];
if(r[i][u] && !bz[u]) minh=min(minh,dis[u]+c[i][u]-dis[i]);
}
if(minh==INF) return 0;
fo(i,S,T)
if(bz[i]) dis[i]+=minh,bz[i]=0;
return 1;
}
int main()
{
freopen("match.in","r",stdin);
freopen("match.out","w",stdout);
int x;
scanf("%d",&n);
S=0,T=n+n+1;
fo(i,1,n)
fo(j,1,n)
{
scanf("%d",&x);
link(i,n+j,1,C-x);
}
fo(i,1,n) link(S,i,1,0),link(n+i,T,1,0);
do
{
while(aug(S,INF)) memset(bz,0,sizeof(bz));
}
while(change());
fo(i,1,n)
fo(j,n+1,n+n)
if(!r[i][j]) b[i]=j;
int t=ans;
printf("%d\n",C*n-t);
fo(k,1,n)
{
fo(i,1,n) r[S][i]=1,r[i][S]=0;
fo(i,1,n)
fo(j,n+1,n+n) r[i][j]=1,r[j][i]=0;
fo(i,n+1,n+n) r[i][T]=1,r[T][i]=0;
r[k][b[k]]=r[b[k]][k]=0;
ans=0;
memset(dis,0,sizeof(dis));
do
{
while(aug(S,INF)) memset(bz,0,sizeof(bz));
}
while(change());
if(t!=ans) printf("%d %d\n",k,b[k]-n);
}
return 0;
}