奔小康赚大钱Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 13795 Accepted Submission(s): 6029 Problem Description 传说在遥远的地方有一个非常富裕的村落,有一天,村长决定进行制度改革:重新分配房子。 Input 输入数据包含多组测试用例,每组数据的第一行输入n,表示房子的数量(也是老百姓家的数量),接下来有n行,每行n个数表示第i个村名对第j间房出的价格(n<=300)。 Output 请对每组数据输出最大的收入值,每组的输出占一行。 Sample Input 2 100 10 15 23 Sample Output 123 |
中文题意我就不解释了吧。
这是KM算法的板子,KM算法如果不明白的话请度娘,算法都很高深啊。。。
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int qwq=0x7fffffff;
int w[1000][1000]; //w数组记录边权值
int line[1000],usex[1000],usey[1000],cx[1000],cy[1000]; //line数组记录右边端点所连的左端点, usex,usey数组记录是否曾访问过,也是判断是否在增广路上,cx,cy数组就是记录点的顶标
int n,ans,m; //n左m右
bool find(int x){
usex[x]=1;
for (int i=1;i<=m;i++){
if ((usey[i]==0)&&(cx[x]+cy[i]==w[x][i])){ //如果这个点未访问过并且它是子图里面的边
usey[i]=1;
if ((line[i]==0)||find(line[i])){ //如果这个点未匹配或者匹配点能更改
line[i]=x;
return true;
}
}
}
return false;
}
int km(){
for (int i=1;i<=n;i++){ //分别对左边点依次匹配
while (true){
int d=qwq;
memset(usex,0,sizeof(usex));
memset(usey,0,sizeof(usey));
if (find(i)) break; //直到成功匹配才换下一个点匹配
for (int j=1;j<=n;j++){
if (usex[j]){
for (int k=1;k<=m;k++)
if (!usey[k]) d=min(d,cx[j]+cy[k]-w[j][k]); //计算d值
}
}
if (d==qwq) return -1;
for (int j=1;j<=n;j++)
if (usex[j]) cx[j]-=d;
for (int j=1;j<=m;j++)
if (usey[j]) cy[j]+=d; //添加新边
}
}
ans=0;
for (int i=1;i<=m;i++)
ans+=w[line[i]][i];
return ans;
}
int main(){
while (~scanf("%d",&n)){
memset(cy,0,sizeof(cy));
memset(w,0,sizeof(w));
memset(cx,0,sizeof(cx));
for (int i=1;i<=n;i++){
int d=0;
for (int j=1;j<=n;j++){
scanf("%d",&w[i][j]);
d=max(d,w[i][j]); //此处顺便初始化左边点的顶标
}
cx[i]=d;
}
m=n;
memset(line,0,sizeof(line));
printf("%d\n",km());
}
return 0;
}