原题链接
规定n * n(<=2 * 10^3)的图,给出每行每列必须存在的最大值,现在要让你填m(<=8 * 10^5)个点,求填完后所填的数值总和的最小值。
分析:
要使值更小,就要让不需要填最大值的点填0。
枚举行列共出现过的值,对于一个值,若某行与某列的“必须存在的最大值”都是该值,那么若在该行该列的交叉点填下该值,则可以同时满足该行与该列,即“省下一个点不用填”,故可让该行与该列连接一条边,最终最大匹配值就是可以“省下”的点数,而在该值情况下求出来的:
“将该值作为最大值的行数+将该值作为最大值的列数-最大匹配数”就刚好是在该图里最少必须填该值的点数。
答案就累加点数与该值的乘积即可。
PS:这题10^9的复杂度居然没超时,看来题目还是要敢想敢写呀~
int a[maxv],b[maxv],match[maxv],v[2100][2100];
vector<int> g1[maxv],g2[maxv],g[maxv];
map<int,bool> vis;
set<int> all;
bool dfs(int x){
for(int i=0,y;i<g[x].size();i++){
if(!vis[y=g[x][i]]){
vis[y]=1;
if(!match[y]||dfs(match[y])){
match[y]=x;return true;
}
}
}
return false;
}
int main(){
int n,m,k,x,y;
LL ans=0,sum=0;
scanf("%d %d %d",&n,&m,&k);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),g1[a[i]].push_back(i),all.insert(a[i]);
for(int i=1;i<=n;i++) scanf("%d",&b[i]),g2[b[i]].push_back(i),all.insert(b[i]);
for(int i=1;i<=m;i++)
scanf("%d %d",&x,&y),v[x][y]=1;
for(set<int>::iterator it=all.begin();it!=all.end();it++){
int i=*it;
sum=0;
for(int j=1;j<=n+n;j++) g[j].clear();
for(int j=0;j<g1[i].size();j++)
for(int o=0;o<g2[i].size();o++){
int now1=g1[i][j],now2=g2[i][o];
if(v[now1][now2]) g[now1].push_back(now2+n),g[now2+n].push_back(now1);
}
memset(match,0,sizeof(match));
for(int j=1;j<=n+n;j++){
vis.clear();
if(dfs(j)) sum++;
}
sum/=2;
ans+=(g1[i].size()+g2[i].size()-sum)*i;
}
printf("%lld",ans);
return 0;
}