https://codingcompetitions.withgoogle.com/kickstart/round/0000000000436140
确实有点巧妙,我还是菜,去年开始ks难度降低了,还只ak过一次,这次这题没写暴力排名掉到700了
比赛的时候想到拆点然后费用流半天不知道怎么办,然而这题用很简单的贪心就能解决
首先我们把每一行每一列拆成2n个点,如果(i,j)有一个-1,那就链接(i,j+n)边权为b[i][j]
那么我们想到如果一条边有一个点的度数是1的话,他就可以被直接算出来,所以其实我们把这个图变成一棵树后,他就可以自动全算出来了
所以就直接最大生成树,表示最大可以留下哪些边不被拆,把剩下的较小的边拆掉
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxl=1010;
int n,m,tot,cnt;ll ans;
int a[maxl][maxl],b[maxl][maxl];
int r[maxl],c[maxl],deg[maxl],f[maxl];
struct ed{int u,v,l;} e[maxl*maxl];
inline void prework()
{
scanf("%d",&n);
for(int i=1;i<=2*n;i++)
f[i]=i,deg[i]=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%d",&a[i][j]);
cnt=0;ans=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
scanf("%d",&b[i][j]);
if(b[i]>0)
e[++cnt]=ed{i,j+n,b[i][j]},ans+=b[i][j];
}
for(int i=1;i<=n;i++)
scanf("%d",&r[i]);
for(int i=1;i<=n;i++)
scanf("%d",&c[i]);
}
inline bool cmp(const ed &a,const ed &b)
{
return a.l>b.l;
}
inline int find(int x)
{
if(f[x]!=x)
f[x]=find(f[x]);
return f[x];
}
inline void mainwork()
{
ll sum=0;
sort(e+1,e+1+cnt,cmp);
for(int i=1;i<=cnt;i++)
{
int u=find(e[i].u),v=find(e[i].v);
if(u!=v)
{
sum+=e[i].l;
f[v]=u;
}
}
ans-=sum;
}
int main()
{
int t;
scanf("%d",&t);
for(int i=1;i<=t;i++)
{
prework();
mainwork();
printf("Case #%d: %lld\n",i,ans);
}
return 0;
}