之前写过了关于普通二分匹配的相关题目了,就是寻找尽量多的边使得任意边连接的两点都没有与其他边相连,而km算法解决的则是在带权的二分图中寻找权值和最大的匹配,可以通过先给无连接的点连上权值为0或者负无穷(求最小权值和)的边,使得问题变成找到权值和最大的完美匹配。

简单来说,KM算法就是先限定好了最终的权值和然后寻找能不能在这个条件下找到完美匹配,给予左右两边的点一个顶标l的概念,只有l[x]+l[y]==w[x][y]的情况下,这条边此时才能进行匹配,一开始顶标的设置为:左边点的顶标为与该点相连的边的最大权值,右边点为0.若能找到完美匹配,则得到答案,否则我们就得修改顶标使得另一些原本不能用来匹配的边可以进行匹配,不过权值和比之前降低了。

A - 奔小康赚大钱 HDU - 2255

裸题

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=305;
const int inf=0x3f3f3f3f;
int n;
int w[maxn][maxn];
int lx[maxn],ly[maxn];
int matched[maxn];
int slack[maxn];
bool s[maxn],t[maxn];bool match(int i){s[i]=1;for(int j=1;j<=n;j++){int cnt=lx[i]+ly[j]-w[i][j];if(cnt==0&&!t[j]){t[j]=1;if(!matched[j]||match(matched[j])){matched[j]=i;return 1;}}else{slack[j]=min(slack[j],cnt);}}return 0;
}void update(){int a=inf;for(int i=1;i<=n;i++){if(!t[i])a=min(a,slack[i]);}for(int i=1;i<=n;i++){if(s[i])lx[i]-=a;if(t[i])ly[i]+=a;}
}void km(){memset(matched,0,sizeof(matched));memset(lx,0,sizeof(lx));memset(ly,0,sizeof(ly));for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){lx[i]=max(lx[i],w[i][j]);}}for(int i=1;i<=n;i++){memset(slack,0x3f,sizeof(slack));while(1){memset(s,0,sizeof(s));memset(t,0,sizeof(t));if(match(i))break;else update();}}
}int main()
{while(~scanf("%d",&n)){for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){scanf("%d",&w[i][j]);}}km();int ans=0;for(int i=1;i<=n;i++){ans+=lx[i];ans+=ly[i];}printf("%d\n",ans);}return 0;
}

B - Going Home HDU - 1533

n个人n个家,给每个人分配一个home使得所有人回家需要的时间总和最小,通过bfs处理出二分图的边权取负之后km便可。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=105;
const int inf=0x3f3f3f3f;
int n,m;
int w[maxn][maxn];
int lx[maxn],ly[maxn];
int matched[maxn];
int slack[maxn];
bool s[maxn],t[maxn];bool match(int i){s[i]=1;for(int j=1;j<=n;j++){int cnt=lx[i]+ly[j]-w[i][j];if(cnt==0&&!t[j]){t[j]=1;if(!matched[j]||match(matched[j])){matched[j]=i;return 1;}}else{slack[j]=min(slack[j],cnt);}}return 0;
}void update(){int a=inf;for(int i=1;i<=n;i++){if(!t[i])a=min(a,slack[i]);}for(int i=1;i<=n;i++){if(s[i])lx[i]-=a;if(t[i])ly[i]+=a;}
}void km(){memset(matched,0,sizeof(matched));memset(lx,0,sizeof(lx));memset(ly,0,sizeof(ly));for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){lx[i]=max(lx[i],w[i][j]);}}for(int i=1;i<=n;i++){memset(slack,0x3f,sizeof(slack));while(1){memset(s,0,sizeof(s));memset(t,0,sizeof(t));if(match(i))break;else update();}}
}
char ma[105][105];
int idd[105][105];
bool vis[105][105];
int dx[4]={1,0,0,-1};
int dy[4]={0,1,-1,0};
struct node{int x,y,step;
};
void bfs(int id,int x,int y){queue<node>que;que.push({x,y,0});vis[x][y]=1;while(!que.empty()){node cnt=que.front();que.pop();if(ma[cnt.x][cnt.y]=='m')w[id][idd[cnt.x][cnt.y]]=-cnt.step;for(int i=0;i<4;i++){int nx=cnt.x+dx[i],ny=cnt.y+dy[i];if(nx>=0&&nx<n&&ny>=0&&ny<m&&!vis[nx][ny]){vis[nx][ny]=1;que.push({nx,ny,cnt.step+1});}}}}int main()
{while(scanf("%d%d",&n,&m),n+m){for(int i=0;i<n;i++){scanf("%s",ma[i]);}int tot=1;for(int i=0;i<n;i++){for(int j=0;j<m;j++){if(ma[i][j]=='m'){idd[i][j]=tot++;}}}tot=1;for(int i=0;i<n;i++){for(int j=0;j<m;j++){if(ma[i][j]=='H'){memset(vis,0,sizeof(vis));bfs(tot++,i,j);}}}n=tot-1;km();int ans=0;for(int i=1;i<=n;i++){ans+=lx[i];ans+=ly[i];}printf("%d\n",-ans);}return 0;
}

C - Interesting Housing Problem HDU - 2426

学生给不同宿舍评价,分配宿舍给学生使得总得分最高,没有被i学生评分或评价为负数的宿舍不能分配给该学生,求能否给所有学生分配宿舍并求总分最高值。
把不能连的边权设为-inf,最后根据匹配边中有没有-inf的边就能简单判断是否有解了。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=505;
const int inf=0x2f2f2f2f;
int n,m,e;
int w[maxn][maxn];
int lx[maxn],ly[maxn];
int matched[maxn];
int slack[maxn];
bool s[maxn],t[maxn];bool match(int i){s[i]=1;for(int j=1;j<=n;j++){int cnt=lx[i]+ly[j]-w[i][j];if(cnt==0&&!t[j]){t[j]=1;if(!matched[j]||match(matched[j])){matched[j]=i;return 1;}}else{slack[j]=min(slack[j],cnt);}}return 0;
}void update(){int a=inf;for(int i=1;i<=n;i++){if(!t[i])a=min(a,slack[i]);}for(int i=1;i<=n;i++){if(s[i])lx[i]-=a;if(t[i])ly[i]+=a;}
}void km(){memset(matched,0,sizeof(matched));for(int i=1;i<=n;i++){lx[i]=-inf;}memset(ly,0,sizeof(ly));for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){lx[i]=max(lx[i],w[i][j]);}}for(int i=1;i<=n;i++){memset(slack,0x3f,sizeof(slack));while(1){memset(s,0,sizeof(s));memset(t,0,sizeof(t));if(match(i))break;else update();}}
}int main()
{int a,b,c;int cas=0;while(~scanf("%d%d%d",&m,&n,&e)){cas++;for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){w[i][j]=-inf;}}for(int i=0;i<e;i++){scanf("%d%d%d",&a,&b,&c);a++;b++;if(c>=0)w[b][a]=c;}if(m>n){printf("Case %d: -1\n",cas);continue;}km();int ans=0;for(int i=1;i<=m;i++){int cnt=w[matched[i]][i];if(cnt!=-inf){ans+=cnt;}else{ans=-1;break;}}printf("Case %d: %d\n",cas,ans);}return 0;
}

D - Special Fish HDU - 3395
比较裸的km

#include <cstdio>
#include<string.h>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=105;
const int inf=0x3f3f3f3f;
int n,m,e;
int w[maxn][maxn];
int lx[maxn],ly[maxn];
int matched[maxn];
int slack[maxn];
bool s[maxn],t[maxn];bool match(int i){s[i]=1;for(int j=1;j<=n;j++){int cnt=lx[i]+ly[j]-w[i][j];if(cnt==0&&!t[j]){t[j]=1;if(!matched[j]||match(matched[j])){matched[j]=i;return 1;}}else{slack[j]=min(slack[j],cnt);}}return 0;
}void update(){int a=inf;for(int i=1;i<=n;i++){if(!t[i])a=min(a,slack[i]);}for(int i=1;i<=n;i++){if(s[i])lx[i]-=a;if(t[i])ly[i]+=a;}
}void km(){memset(matched,0,sizeof(matched));memset(lx,0,sizeof(lx));memset(ly,0,sizeof(ly));for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){lx[i]=max(lx[i],w[i][j]);}}for(int i=1;i<=n;i++){memset(slack,0x3f,sizeof(slack));while(1){memset(s,0,sizeof(s));memset(t,0,sizeof(t));if(match(i))break;else update();}}
}int val[105];
int main()
{char str[105];while(~scanf("%d",&n),n){memset(w,0,sizeof(w));for(int i=1;i<=n;i++){scanf("%d",&val[i]);}for(int i=0;i<n;i++){scanf("%s",str);for(int j=0;j<n;j++){if(str[j]=='1')w[i+1][j+1]=val[i+1]^val[j+1];}}km();int ans=0;for(int i=1;i<=n;i++){ans+=lx[i];ans+=ly[i];}printf("%d\n",ans);}return 0;
}

E - Chocolate HDU - 2282

巧克力可以移动到相邻的盒子里,求使所有盒子里都只有不多于一个巧克力所需要的最小移动次数。
处理巧克力与所有盒子的距离建边取负即可,求距离需要注意。。

#include <cstdio>
#include<string.h>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=505;
const int inf=0x2f2f2f2f;
int n,m,e;
int w[maxn][maxn];
int lx[maxn],ly[maxn];
int matched[maxn];
int slack[maxn];
bool s[maxn],t[maxn];
//w cal wa,lx's init wa
bool match(int i){s[i]=1;for(int j=1;j<=n;j++){int cnt=lx[i]+ly[j]-w[i][j];if(cnt==0&&!t[j]){t[j]=1;if(!matched[j]||match(matched[j])){matched[j]=i;return 1;}}else{slack[j]=min(slack[j],cnt);}}return 0;
}void update(){int a=inf;for(int i=1;i<=n;i++){if(!t[i])a=min(a,slack[i]);}for(int i=1;i<=n;i++){if(s[i])lx[i]-=a;if(t[i])ly[i]+=a;}
}void km(){memset(matched,0,sizeof(matched));// memset(lx,0,sizeof(lx));for(int i=1;i<=n;i++)lx[i]=-inf;memset(ly,0,sizeof(ly));for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){lx[i]=max(lx[i],w[i][j]);}}for(int i=1;i<=n;i++){memset(slack,0x3f,sizeof(slack));while(1){memset(s,0,sizeof(s));memset(t,0,sizeof(t));if(match(i))break;else update();}}
}int num[505];
int main()
{while(~scanf("%d",&n)){int tot=1;memset(w,0,sizeof(w));for(int i=1;i<=n;i++){scanf("%d",&num[i]);}for(int i=1;i<=n;i++){int cnt=num[i];for(int j=1;j<=cnt;j++){for(int k=1;k<=n;k++){w[tot][k]=-min(abs(k-i),n-abs(i-k));}tot++;}}km();int ans=0;for(int i=1;i<=n;i++){ans+=lx[i],ans+=ly[i];}printf("%d\n",-ans);}return 0;
}

F - One fihgt one HDU - 2813

裸题,处理名字转化为编号进行建边就行,我用的map处理

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=205;
const int inf=0x3f3f3f3f;
int n,m,k;
int w[maxn][maxn];
int lx[maxn],ly[maxn];
int matched[maxn];
int slack[maxn];
bool s[maxn],t[maxn];
//slack的更新错误
bool match(int i){s[i]=1;for(int j=1;j<=m;j++){int cnt=lx[i]+ly[j]-w[i][j];if(cnt==0&&!t[j]){t[j]=1;if(!matched[j]||match(matched[j])){matched[j]=i;return 1;}}else{slack[j]=min(slack[j],cnt);}}return 0;
}void update(){int a=inf;for(int i=1;i<=m;i++){if(!t[i])a=min(a,slack[i]);}for(int i=1;i<=n;i++){if(s[i])lx[i]-=a;}for(int i=1;i<=m;i++){if(t[i])ly[i]+=a;}
}void km(){memset(matched,0,sizeof(matched));memset(lx,-inf,sizeof(lx));memset(ly,0,sizeof(ly));for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){lx[i]=max(lx[i],w[i][j]);}}for(int i=1;i<=n;i++){memset(slack,0x3f,sizeof(slack));while(1){memset(s,0,sizeof(s));memset(t,0,sizeof(t));if(match(i))break;else update();}}
}int main()
{while(~scanf("%d%d%d",&n,&m,&k)){memset(w,-inf,sizeof(w));char na[25],nb[25];int tota=1,totb=1,c;map<string,int> ma;for(int i=0;i<k;i++){scanf("%s%s%d",na,nb,&c);if(ma.find(na)==ma.end())ma[na]=tota++;if(ma.find(nb)==ma.end())ma[nb]=totb++;int cnta=ma[na],cntb=ma[nb];w[cnta][cntb]=-c;}km();int ans=0;for(int i=1;i<=m;i++){if(matched[i]&&matched[i]<=n){ans+=w[matched[i]][i];}}printf("%d\n",-ans);}
}

G - Cyclic Tour HDU - 1853

寻找环使得每个点都被访问过且路径权值和最小
只要每个点的入度和出度都为1就可以成环了,注意重边。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=105;
const int inf=0x3f3f3f3f;
int n,m,k;
int w[maxn][maxn];
int lx[maxn],ly[maxn];
int matched[maxn];
int slack[maxn];
bool s[maxn],t[maxn];
int head[maxn];
//cheng huan tiao jian cuowu && chongbian
bool match(int i){s[i]=1;for(int j=1;j<=m;j++){int cnt=lx[i]+ly[j]-w[i][j];if(cnt==0&&!t[j]){t[j]=1;if(!matched[j]||match(matched[j])){matched[j]=i;head[j]=head[i];return 1;}}else{slack[j]=min(slack[j],cnt);}}return 0;
}void update(){int a=inf;for(int i=1;i<=m;i++){if(!t[i])a=min(a,slack[i]);}for(int i=1;i<=n;i++){if(s[i])lx[i]-=a;}for(int i=1;i<=m;i++){if(t[i])ly[i]+=a;}
}void km(){memset(matched,0,sizeof(matched));memset(lx,-0x3f,sizeof(lx));memset(ly,0,sizeof(ly));for(int i=1;i<=n;i++){head[i]=i;for(int j=1;j<=m;j++){lx[i]=max(lx[i],w[i][j]);}}for(int i=1;i<=n;i++){memset(slack,0x3f,sizeof(slack));while(1){memset(s,0,sizeof(s));memset(t,0,sizeof(t));if(match(i))break;else update();}}
}int main()
{int a,b,c;while(~scanf("%d%d",&n,&k)){m=n;memset(w,-0x3f,sizeof(w));for(int i=0;i<k;i++){scanf("%d%d%d",&a,&b,&c);if(-c>w[a][b])w[a][b]=-c;}km();int ans=0;for(int i=1;i<=n;i++){int cnt=w[matched[i]][i];if(cnt==w[0][0]){ans=1;break;}ans+=cnt;}printf("%d\n",-ans);}
}

H - Tour HDU - 3488

跟上题一样

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=205;
const int inf=0x3f3f3f3f;
int n,m,k;
int w[maxn][maxn];
int lx[maxn],ly[maxn];
int matched[maxn];
int slack[maxn];
bool s[maxn],t[maxn];
int head[maxn];bool match(int i){s[i]=1;for(int j=1;j<=m;j++){int cnt=lx[i]+ly[j]-w[i][j];if(cnt==0&&!t[j]){t[j]=1;if(!matched[j]||match(matched[j])){matched[j]=i;head[j]=head[i];return 1;}}else{slack[j]=min(slack[j],cnt);}}return 0;
}void update(){int a=inf;for(int i=1;i<=m;i++){if(!t[i])a=min(a,slack[i]);}for(int i=1;i<=n;i++){if(s[i])lx[i]-=a;}for(int i=1;i<=m;i++){if(t[i])ly[i]+=a;}
}void km(){memset(matched,0,sizeof(matched));memset(lx,-0x3f,sizeof(lx));memset(ly,0,sizeof(ly));for(int i=1;i<=n;i++){head[i]=i;for(int j=1;j<=m;j++){lx[i]=max(lx[i],w[i][j]);}}for(int i=1;i<=n;i++){memset(slack,0x3f,sizeof(slack));while(1){memset(s,0,sizeof(s));memset(t,0,sizeof(t));if(match(i))break;else update();}}
}int main()
{int a,b,c;int t;scanf("%d",&t);while(t--){scanf("%d%d",&n,&k);m=n;memset(w,-0x3f,sizeof(w));for(int i=0;i<k;i++){scanf("%d%d%d",&a,&b,&c);if(-c>w[a][b])w[a][b]=-c;}km();int ans=0;for(int i=1;i<=n;i++){int cnt=w[matched[i]][i];if(cnt==w[0][0]){ans=1;break;}ans+=cnt;}printf("%d\n",-ans);}
}

I - A new Graph Game HDU - 3435

找哈密顿图,使用的代码跟上面两题一样,但感觉哈密顿图不应该只有一个环吗?

J - Card Game HDU - 3722

也是比较裸的题,按题意处理边权即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1006;
const int inf=0x3f3f3f3f;
int n,m,k;
int w[maxn][maxn];
int lx[maxn],ly[maxn];
int matched[maxn];
int slack[maxn];
bool s[maxn],t[maxn];
//selfloop always equals to 0 && lower mistake
bool match(int i){s[i]=1;for(int j=1;j<=m;j++){int cnt=lx[i]+ly[j]-w[i][j];if(cnt==0&&!t[j]){t[j]=1;if(!matched[j]||match(matched[j])){matched[j]=i;return 1;}}else{slack[j]=min(slack[j],cnt);}}return 0;
}void update(){int a=inf;for(int i=1;i<=m;i++){if(!t[i])a=min(a,slack[i]);}for(int i=1;i<=n;i++){if(s[i])lx[i]-=a;}for(int i=1;i<=m;i++){if(t[i])ly[i]+=a;}
}void km(){memset(matched,0,sizeof(matched));memset(lx,0,sizeof(lx));memset(ly,0,sizeof(ly));for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){lx[i]=max(lx[i],w[i][j]);}}for(int i=1;i<=n;i++){memset(slack,0x3f,sizeof(slack));while(1){memset(s,0,sizeof(s));memset(t,0,sizeof(t));if(match(i))break;else update();}}
}
char str[205][1001];
int cal(char *a,char *b){int lena=strlen(a);int lenb=strlen(b);int i=0;for(;i<lena&&i<lenb;i++){if(b[i]!=a[lena-1-i])break;}return i;
}int main()
{while(~scanf("%d",&n)){m=n;memset(w,0,sizeof(w));for(int i=1;i<=n;i++)scanf("%s",str[i]);for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){if(i==j)w[i][j]=0;else w[i][j]=cal(str[i],str[j]);}}km();int ans=0;for(int i=1;i<=n;i++){ans+=lx[i],ans+=ly[i];}printf("%d\n",ans);}
}

K - Similarity HDU - 3718

对点分类,每个人分类用的编号不同,求两组答案最大的相似度为多少,也是处理建边就行。用map似乎超时?直接开[26][26]的数组做就行。

#include<cstdio>
#include<algorithm>
#include<string.h>
#include<map>
using namespace std;
typedef long long ll;
const int maxn = 30;
const int inf = 0x3f3f3f3f;
int n, m, k, m1;
int w[maxn][maxn];
int lx[maxn], ly[maxn];
int matched[maxn];
int slack[maxn];
bool s[maxn], t[maxn];
bool match(int i){s[i] = 1;for (int j = 1; j <= m; j++){int cnt = lx[i] + ly[j] - w[i][j];if (cnt == 0 && !t[j]){t[j] = 1;if (!matched[j] || match(matched[j])){matched[j] = i;return 1;}}else{slack[j] = min(slack[j], cnt);}}return 0;
}void update(){int a = inf;for (int i = 1; i <= m; i++){if (!t[i])a = min(a, slack[i]);}for (int i = 1; i <= n; i++){if (s[i])lx[i] -= a;}for (int i = 1; i <= m; i++){if (t[i])ly[i] += a;}
}void km(){memset(matched, 0, sizeof(matched));memset(lx, 0, sizeof(lx));memset(ly, 0, sizeof(ly));for (int i = 1; i <= n; i++){for (int j = 1; j <= m; j++){lx[i] = max(lx[i], w[i][j]);}}for (int i = 1; i <= n; i++){memset(slack, 0x3f, sizeof(slack));while (1){memset(s, 0, sizeof(s));memset(t, 0, sizeof(t));if (match(i))break;else update();}}
}int s1[10005];
int main()
{int t;scanf("%d", &t);while (t--){scanf("%d%d%d", &n, &k, &m1);int aass=n;double n1 = n;int tot1 = 0, tot2;char c[2];for (int i = 0; i<n; i++){scanf("%s", c);char cnt = c[0];s1[i] =cnt-'A'+1;}for (int i = 0; i<m1; i++){memset(w, 0, sizeof(w));tot2 = 0;for (int j = 0; j<aass; j++){scanf("%s", c);char cnt = c[0];w[s1[j]][cnt - 'A'+1]++;}n = m = 26;km();double ans = 0;for (int j = 1; j <= m; j++){if (matched[j]){ans += w[matched[j]][j];}}ans = ans / n1;printf("%.4lf\n", ans);}}return 0;
}

L - Mining Station on the Sea HDU - 2448

船返回港口的问题,用floyd处理出所有点对间的最短路来建边,需要注意的是船入港后不能再驶出,所以从港口连出的边边权得为inf,否则会有船的最短路是经过港口的。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 105;
const int inf = 0x3f3f3f3f;
int n, m, m1, k, p;
int w[maxn][maxn];
int lx[maxn], ly[maxn];
int matched[maxn];
int slack[maxn];
bool s[maxn], t[maxn];
//入港口后就不能出港口了,dis的更新错误
bool match(int i){s[i] = 1;for (int j = 1; j <= m; j++){int cnt = lx[i] + ly[j] - w[i][j];if (cnt == 0 && !t[j]){t[j] = 1;if (!matched[j] || match(matched[j])){matched[j] = i;return 1;}}else{slack[j] = min(slack[j], cnt);}}return 0;
}void update(){int a = inf;for (int i = 1; i <= m; i++){if (!t[i])a = min(a, slack[i]);}for (int i = 1; i <= n; i++){if (s[i])lx[i] -= a;}for (int i = 1; i <= m; i++){if (t[i])ly[i] += a;}
}void km(){memset(matched, 0, sizeof(matched));memset(lx, -0x3f, sizeof(lx));memset(ly, 0, sizeof(ly));for (int i = 1; i <= n; i++){for (int j = 1; j <= m; j++){lx[i] = max(lx[i], w[i][j]);}}for (int i = 1; i <= n; i++){memset(slack, 0x3f, sizeof(slack));while (1){memset(s, 0, sizeof(s));memset(t, 0, sizeof(t));if (match(i))break;else update();}}
}int id[105];
int dis[305][305];
int main()
{int a,b,c;while(~scanf("%d%d%d%d",&n,&m1,&k,&p)){m=n;memset(w,-0x3f,sizeof(w));memset(dis,0x3f,sizeof(dis));for(int i=0;i<n;i++){scanf("%d",&id[i]);id[i]--;}for(int i=0;i<k;i++){scanf("%d%d%d",&a,&b,&c);a--,b--;dis[a][b]=min(dis[a][b],c);dis[b][a]=dis[a][b];}for(int i=0;i<p;i++){scanf("%d%d%d",&a,&b,&c);a--;b--;// dis[m1+a][b]=min(dis[m1+a][b],c);dis[b][m1+a]=min(dis[b][m1+a],c);}for(int k=0;k<n+m1;k++){for(int i=0;i<n+m1;i++){for(int j=0;j<n+m1;j++){dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);}}}for(int i=0;i<n;i++){int cnt=id[i];for(int j=0;j<n;j++){w[i+1][j+1]=-dis[cnt][m1+j];}}km();int ans=0;for(int i=1;i<=n;i++){ans+=lx[i],ans+=ly[i];}printf("%d\n",-ans);}return 0;
}

M - Assignment HDU - 2853

题意看起来就是很裸的最大边权二分匹配,但是要尽量不改变原来的匹配,所以需要 对边权方法然后使原匹配边的边权+1,这样就会尽量不改变原匹配边了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 55;
const int inf = 0x3f3f3f3f;
int n, m, m1, k, p;
int w[maxn][maxn];
int lx[maxn], ly[maxn];
int matched[maxn];
int slack[maxn];
bool s[maxn], t[maxn];
int chose[maxn];
//需要放大权值并将原匹配边的权值+1,使得尽量匹配元匹配边
bool match(int i){s[i] = 1;for (int j = 0; j <= m; j++){int cnt = lx[i] + ly[j] - w[i][j];if (cnt == 0 && !t[j]){t[j] = 1;if (!matched[j] || match(matched[j])){matched[j] = i;return 1;}}else{slack[j] = min(slack[j], cnt);}}return 0;
}void update(){int a = inf;for (int i = 1; i <= m; i++){if (!t[i])a = min(a, slack[i]);}for (int i = 1; i <= n; i++){if (s[i])lx[i] -= a;}for (int i = 1; i <= m; i++){if (t[i])ly[i] += a;}
}void km(){memset(matched, 0, sizeof(matched));memset(lx, 0, sizeof(lx));memset(ly, 0, sizeof(ly));for (int i = 1; i <= n; i++){for (int j = 1; j <= m; j++){lx[i] = max(lx[i], w[i][j]);}}for (int i = 1; i <= n; i++){memset(slack, 0x3f, sizeof(slack));while (1){memset(s, 0, sizeof(s));memset(t, 0, sizeof(t));if (match(i))break;else update();}}
}int main()
{while(~scanf("%d%d",&n,&m)){for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){scanf("%d",&w[i][j]);w[i][j]*=200;}}for(int i=1;i<=n;i++){scanf("%d",&chose[i]);w[i][chose[i]]++;}int old=0;for(int i=1;i<=n;i++){old+=w[i][chose[i]]/200;}km();int ans=0;int num=0;for(int i=1;i<=m;i++){if(matched[i]){if(chose[matched[i]]!=i)num++;ans+=w[matched[i]][i]/200;}}ans-=old;printf("%d %d\n",num,ans);}return 0;
}

N - My Brute HDU - 3315

跟上面那题差不多的操作。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 100;
const int inf = 0x3f3f3f3f;
int n, m;
int w[maxn][maxn];
int lx[maxn], ly[maxn];
int matched[maxn];
int slack[maxn];
bool s[maxn], t[maxn];
int chose[maxn];bool match(int i){s[i] = 1;for (int j = 0; j <= m; j++){int cnt = lx[i] + ly[j] - w[i][j];if (cnt == 0 && !t[j]){t[j] = 1;if (!matched[j] || match(matched[j])){matched[j] = i;return 1;}}else{slack[j] = min(slack[j], cnt);}}return 0;
}void update(){int a = inf;for (int i = 1; i <= m; i++){if (!t[i])a = min(a, slack[i]);}for (int i = 1; i <= n; i++){if (s[i])lx[i] -= a;}for (int i = 1; i <= m; i++){if (t[i])ly[i] += a;}
}void km(){memset(matched, 0, sizeof(matched));memset(lx, -0x3f, sizeof(lx));memset(ly, 0, sizeof(ly));for (int i = 1; i <= n; i++){for (int j = 1; j <= m; j++){lx[i] = max(lx[i], w[i][j]);}}for (int i = 1; i <= n; i++){memset(slack, 0x3f, sizeof(slack));while (1){memset(s, 0, sizeof(s));memset(t, 0, sizeof(t));if (match(i))break;else update();}}
}int v[maxn],h[maxn],p[maxn],a[maxn],b[maxn];bool win(int i,int j){int cnta=h[i],cntb=p[j];int aa=a[i],bb=b[j];while(cnta>0&&cntb>0){cntb-=aa;if(cntb<=0)break;cnta-=bb;}if(cnta<=0)return 0;else return 1;
}int main()
{while(~scanf("%d",&n),n){m=n;for(int i=1;i<=n;i++){scanf("%d",&v[i]);v[i]*=100;}for(int i=1;i<=n;i++)scanf("%d",&h[i]);for(int i=1;i<=n;i++)scanf("%d",&p[i]);for(int i=1;i<=n;i++)scanf("%d",&a[i]);for(int i=1;i<=n;i++)scanf("%d",&b[i]);for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){int cntx=h[i]/b[j];if(h[i]%b[j])cntx++;if(cntx*a[i]>=p[j]){w[i][j]=v[i];}else w[i][j]=-v[i];if(i==j){w[i][j]++;}}}km();int ans=0;int num=0;for(int i=1;i<=n;i++){int cnt=matched[i];if(cnt!=i)num++;ans+=w[cnt][i];}num=n-num;double gg=double(num)/n;gg*=100;if(ans>0){printf("%d %.3lf%%\n",ans/100,gg);}else printf("Oh, I lose my dear seaco!\n");}return 0;
}

大部分的km题需要处理的都只是边的建立,真正匹配的算法都不需要改变,但是理解记忆下次能直接实现还是比较好的。

KM算法 入门——[kuangbin]KM匹配相关推荐

  1. HDU(2255),KM算法,最大权匹配

    题目链接 奔小康赚大钱 Time Limit: 1000/1000MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Su ...

  2. KM算法--带权二分匹配

    http://acm.hdu.edu.cn/showproblem.php?pid=2255 问题概述:有n个人,n个房子,每个人对每个房子出价都不同,你是村长,你如何分配房子才能获得最高收益? 输入 ...

  3. km算法c语言,KM算法最好的讲解+POJ2195[KM算法+最小费用流]

    二.KM算法: 二分图最优匹配:对于二分图的每条边都有一个权(非负),要求一种完备匹配方案,使得所有匹配边的权和最大,记做最优完备匹配.(特殊的,当所有边的权为1时,就是最大完备匹配问题) 解二分图最 ...

  4. KM算法(最优匹配)

    hdu2255 奔小康赚大钱 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) T ...

  5. 【HDU 2255】奔小康赚大钱 (最佳二分匹配KM算法)

    奔小康赚大钱 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Sub ...

  6. 带你入门多目标跟踪(三)匈牙利算法KM算法

    匈牙利算法(Hungarian Algorithm)与KM算法(Kuhn-Munkres Algorithm)是做多目标跟踪的小伙伴很容易在论文中见到的两种算法.他们都是用来解决多目标跟踪中的数据关联 ...

  7. KM算法 最优匹配(最大权匹配) hdu 2255 奔小康赚大钱 最小权匹配 poj 2195 Going Home

    最大权二分匹配问题就是给二分图的每条边一个权值,选择若干不相交的边,得到的总权值最大.解决这个问题可以用KM算法.理解KM算法需要首先理解"可行顶标"的概念.可行顶标是指关于二分图 ...

  8. KM算法解决二分图最大权分配问题

    匈牙利算法和KM算法都可用来解决任务分配问题(亦称指派问题):假设有n名员工以及n份工作,一个人只能完成一项任务且能完成的任务各不相同.问如何安排员工才能使效率达到最大. 用大白话来描述二分图:二分图 ...

  9. poj 3565 uva 1411 Ants KM算法求最小权

    由于涉及到实数,一定,一定不能直接等于,一定,一定加一个误差<0.00001,坑死了-- 有两种事物,不难想到用二分图.这里涉及到一个有趣的问题,这个二分图的完美匹配的最小权值和就是答案.为啥呢 ...

最新文章

  1. 工信部 学习类app_工信部整治APP侵权行为,私自收集个人信息等8类问题被点名...
  2. 进行三万多次地震训练后,他们发现了快速预测震动强度的新方法
  3. 一起谈.NET技术,微软PDC10:大牛谈ASP.NET和C#技术走向
  4. delete from t引发的血案
  5. KNN算法--手写输入判断(Python3)
  6. MobileNet V2 复现
  7. java解决策略膨胀_折腾Java设计模式之策略模式
  8. linux一g运行内存不足,在linux运行weblogic出现运行内存不足错误,求鞭挞....
  9. 玩转短视频?守护视频安全?AI智能提速?一分钱体验? 阿里云视频点播大招盘点...
  10. 网站如何进行渠道跟踪_网站前期要如何进行SEO优化?
  11. 20200123每日一句
  12. win7旗舰版升级win10
  13. oracle获取两个月前的时间
  14. Android——文件存储之外部存储
  15. 费马小定理、欧拉定理总结
  16. CSS限制字数,超出部份显示点点点...
  17. python fork()创建新的进程,daemon进程
  18. Pixelmator for Mac如何使用抠..图的功能
  19. 亚马逊获20亿美元信用额度:有助新业务投资
  20. 用X64 Native tools command promt for vs安装ROS

热门文章

  1. 刚刚结束Autodesk 2013 DevDays 北京和上海的会议
  2. 让ChatGPT干正事,如何查找靠谱的真文献写论文
  3. 数据为王的时代,NLP数据产业的未来
  4. Win11安装ise14.7(最终解决方案)——Ubuntu18.04安装ISE与modelsim
  5. 炸学校视频--小时候唱的
  6. 2155 Problem Description(floyd算法变型)
  7. 读书计划 现在开始……
  8. 怎么查看计算机的配置?
  9. PC机上生成sha256等校验值
  10. iOS,不能使用UDID之后