最大流问题,关键是建模。
因为要把垃圾归类,画成图的形式,就是二分图最佳匹配。
解决二分图最佳匹配,选择最小费用最大流,也可以用EK 但是前者复杂度小点。
建立超级源点跟超级汇点。
#include<bits/stdc++.h>
#define MAXN 0x3f3f3f3f
using namespace std;
const int maxn=500;
int n,s=1,t;
int c[maxn][maxn],g[maxn][maxn],d[maxn];
int pre[maxn];
bool vis[maxn];
int p=0;
void augment(){
int i=t;
int a=MAXN;
while(i>s){
if(c[pre[i]][i]<a) a=c[pre[i]][i];
i=pre[i];
}
i=t;
while(i>s){
c[pre[i]][i]-=a;
c[i][pre[i]]+=a;
i=pre[i];
}
}
bool spfa(){
memset(vis,0,sizeof(vis));
memset(pre,0,sizeof(pre));
queue<int> q;
for(int i=1;i<=t;i++){
d[i]=MAXN;
}
q.push(s);
vis[s]=1;
d[s]=0;
while(!q.empty()){
int x=q.front();
q.pop();
vis[x]=0;
for(int i=1;i<=t;i++){
if(d[i]>d[x]+g[x][i]&&c[x][i]>0){
d[i]=d[x]+g[x][i];
pre[i]=x;
if(!vis[i]){
vis[i]=1;
q.push(i);
}
}
}
}
if(d[t]!=(int)MAXN){
return 1;
}
return 0;
}
int main()
{
(ios::sync_with_stdio(false),cin.tie(0));
cin>>n;
t=2*n+2;
for(int i=1;i<=n;i++){
int sum=0;
for(int j=1;j<=n;j++){
c[i+1][1+n+j]=1;
cin>>g[1+i][1+n+j];
sum+=g[1+i][1+n+j];
}
for(int j=1;j<=n;j++){
g[1+i][1+n+j]=sum-g[1+i][1+n+j];
g[1+n+j][1+i]=-g[1+i][1+n+j];
}
}
for(int i=1;i<=n;i++){
c[1][1+i]=1;
c[1+n+i][t]=1;
}
while(spfa()){
augment();
p+=d[t];
}
cout<<p<<"\n";
return 0;
}
/*
*/
EK算法:
#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
const int maxn=200;
int w[maxn][maxn];
int lx[maxn],ly[maxn];
bool visx[maxn],visy[maxn];
int link[maxn];
int n;
int dfs(int t){
visx[t]=1;
for(int i=1;i<=n;i++){
if(!visy[i]&&lx[t]+ly[i]==w[t][i]){
visy[i]=1;
if(link[i]==-1||dfs(link[i])){
link[i]=t;
return true;
}
}
}
return false;
}
int km(){
int sum=0;
memset(ly,0,sizeof(ly));
for(int i=1;i<=n;i++){
lx[i]=-INF;
for(int j=1;j<=n;j++){
lx[i]=max(lx[i],w[i][j]);
}
}
memset(link,-1,sizeof(link));
for(int i=1;i<=n;i++){
while(true){
memset(visx,0,sizeof(visx));
memset(visy,0,sizeof(visy));
if(dfs(i)) break;
int d=INF;
for(int j=1;j<=n;j++){
if(visx[j])
for(int k=1;k<=n;k++)
if(!visy[k])
d=min(d,lx[j]+ly[k]-w[j][k]);
}
if(d==INF){
return -1;
}
for(int j=1;j<=n;j++){
if(visx[j]) lx[j]-=d;
}
for(int j=1;j<=n;j++){
if(visy[j]) ly[j]+=d;
}
}
}
for(int i=1;i<=n;i++){
if(link[i]>-1) sum+=w[link[i]][i];
}
return sum;
}
int main(){
int ans=0;
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cin>>w[i][j];
ans+=w[i][j];
}
}
ans-=km();
cout<<ans<<endl;
return 0;
}