P4014 分配问题
题目描述
有 \(n\) 件工作要分配给 \(n\) 个人做。第 \(i\) 个人做第 \(j\) 件工作产生的效益为 \(c_{ij}\) 。试设计一个将 \(n\) 件工作分配给 \(n\) 个人做的分配方案,使产生的总效益最大。
输入输出格式
输入格式
文件的第 \(1\) 行有 \(1\) 个正整数 \(n\),表示有 \(n\) 件工作要分配给 \(n\) 个人做。
输出格式
两行分别输出最小总效益和最大总效益。
输入输出样例
输入样例 #1
5
2 2 2 1 2
2 3 1 2 4
2 0 1 1 1
2 3 4 3 3
输出样例 #1
5 14
显然是KM算法(下文是优化),先要将负的权值传进去,输出,再初始化,再传正的权值进去,再输出
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
typedef long long ll;
const ll Maxn=505;
const ll inf=1e18;
ll n,m,map[Maxn][Maxn],matched[Maxn];
ll slack[Maxn],pre[Maxn],ex[Maxn],ey[Maxn];
ll a[105][105];
bool visx[Maxn],visy[Maxn];
void match(ll u)
{
ll x,y=0,yy=0,delta;
memset(pre,0,sizeof(pre));
for(ll i=1;i<=n;i++)slack[i]=inf;
matched[y]=u;
while(1)
{
x=matched[y];delta=inf;visy[y]=1;
for(ll i=1;i<=n;i++)
{
if(visy[i])continue;
if(slack[i]>ex[x]+ey[i]-map[x][i])
{
slack[i]=ex[x]+ey[i]-map[x][i];
pre[i]=y;
}
if(slack[i]<delta){delta=slack[i];yy=i;}
}
for(ll i=0;i<=n;i++)
{
if(visy[i])ex[matched[i]]-=delta,ey[i]+=delta;
else slack[i]-=delta;
}
y=yy;
if(matched[y]==-1)break;
}
while(y){matched[y]=matched[pre[y]];y=pre[y];}
}
ll KM()
{
memset(matched,-1,sizeof(matched));
memset(ex,0,sizeof(ex));
memset(ey,0,sizeof(ey));
for(ll i=1;i<=n;i++)
{
memset(visy,0,sizeof(visy));
match(i);
}
ll res=0;
for(ll i=1;i<=n;i++)
if(matched[i]!=-1)res+=map[matched[i]][i];
return res;
}
int main()
{
ll w;
scanf("%lld",&n);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
map[i][j]=-inf;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
scanf("%lld",&map[i][j]);
}
for(ll i=1;i<=n;i++)
for (ll j=1;j<=n;j++)
map[i][j]=-1*map[i][j];
printf("%lld\n",-1*KM());
for(ll i=1;i<=n;i++)
for (ll j=1;j<=n;j++)
map[i][j]=-1*map[i][j];
printf("%lld\n",KM());
return 0;
}