KM小练
来源:互联网 发布:html表白网页源码 编辑:程序博客网 时间:2024/06/07 00:33
1、题目:
HDU1853 Cyclic Tour
题意:
给出一个n个点m条边的有向图,每一条边有边权,从这个图中选出若干个环,使每一个点属于且仅属于一个环,并且使环的权值和最小。
题解:
看到这个东西要想到拆点,然后就用KM求个最大带权匹配就好了,因为要求最小值,就把所有的变成负数求最大值再倒过来
注意如果发现一组不能匹配就直接返回,不要继续累加,不然容易爆int而变成一个极大的负数
代码:
#include <cstdio>#include <cstring>#include <iostream>#define INF 1e9using namespace std;int n,vis[205],lx[105],ly[105],e[105][105],belong[105];int find(int i,int id){ vis[i]=id; for (int j=1;j<=n;j++) if (vis[j+n]!=id && lx[i]+ly[j]==e[i][j]) { vis[j+n]=id; if (!belong[j] || find(belong[j],id)) { belong[j]=i; return 1; } } return 0;}void change(int id){ int i,j,a=INF; for (i=1;i<=n;i++) if (vis[i]==id) for (j=1;j<=n;j++) if (vis[j+n]!=id) a=min(a,lx[i]+ly[j]-e[i][j]); for (i=1;i<=n;i++) { if (vis[i]==id) lx[i]-=a; if (vis[i+n]==id) ly[i]+=a; }}int KM(){ int i,j,ans=0,id=0; memset(vis,0,sizeof(vis)); for (i=1;i<=n;i++) { belong[i]=0; lx[i]=-INF;ly[i]=0; for (j=1;j<=n;j++) lx[i]=max(lx[i],e[i][j]); } for (i=1;i<=n;i++) while (1) { ++id; if (find(i,id)) break; else change(id); } for (i=1;i<=n;i++) if (e[belong[i]][i]<-INF) return 1;else ans+=e[belong[i]][i]; return ans;}int main(){ int m,i; while (scanf("%d%d",&n,&m)!=EOF) { memset(e,128,sizeof(e)); for (i=1;i<=m;i++) { int x,y,z; scanf("%d%d%d",&x,&y,&z); e[x][y]=max(e[x][y],-z); } printf("%d\n",-KM()); }}
2、题目:
HDU2426 Interesting Housing Problem
题意:
有n个学生,m个房间,每一个学生对一些房间有一个评分,求使所有的学生每一个人分配一个房间并使评分最大
注意这道题学生不会分配一个没有评分或者评分为负数的房间
题解:
这道题调了好久啊,最后发现是初始化出了问题,果然m和n不相等很麻烦。。
别的就是一样的KM最大匹配,注意因为不是每一个房间都是有人住,要记录住上宿舍的学生个数,看看是不是n
这是一种优化的KM,其实是一样的,只不过在find的时候就维护了顶标,效率高出一倍呢,以后还是要写这种啊
代码:
#include <cstdio>#include <cstring>#include <iostream>#define INF 1e9using namespace std;int vis[1005],w[505][505],belong[505],lx[505],ly[505],n,m,delta[505];bool find(int i,int id){ vis[i]=id; for (int j=1;j<=m;j++) if (vis[j+n]!=id) { if (lx[i]+ly[j]==w[i][j]) { vis[j+n]=id; if (!belong[j] || find(belong[j],id)) { belong[j]=i; return 1; } }else delta[j]=min(delta[j],lx[i]+ly[j]-w[i][j]); } return 0;}int KM(){ int i,j,ans=0,ii,id=0; memset(vis,0,sizeof(vis)); for (i=1;i<=n;i++) { lx[i]=-INF; for (j=1;j<=m;j++) lx[i]=max(lx[i],w[i][j]),ly[j]=0,belong[j]=0; } for (ii=1;ii<=n;ii++) { for (j=1;j<=m;j++) delta[j]=INF; while (1) { ++id; if (find(ii,id)) break; int a=INF; for (i=1;i<=m;i++) if (vis[i+n]!=id) a=min(a,delta[i]); for (i=1;i<=n;i++) if (vis[i]==id) lx[i]-=a; for (i=1;i<=m;i++) if (vis[i+n]==id) ly[i]+=a; else delta[i]-=a; } } int xs=0; for (i=1;i<=m;i++) if (belong[i] && w[belong[i]][i]>-INF) ans+=w[belong[i]][i],xs++; if (xs<n) return -1; return ans;}int main(){ int num=0,e,i; while (~scanf("%d%d%d",&n,&m,&e)) { memset(w,0x8f,sizeof(w)); for (i=1;i<=e;i++) { int x,y,z; scanf("%d%d%d",&x,&y,&z); if (z<0) continue; x++;y++;w[x][y]=z; } printf("Case %d: %d\n",++num,KM()); }}
3、题目:
POJ3565 Ants
题意:
在一个二维平面上给出n个黑点和n个白点,求一种方案使黑点白点配对形成的n条线段不相交。
题解:
相交?这个KM有什么关系呢?但是让我们画个图来体会一下
我们就是害怕出现蓝边这种情况啊,我们比较喜欢粉边这一种呢
但是你可以发现了吧?蓝边相加一定大于粉边啊,所以我们只需要找最小边匹配就好了!
还有一个小细节,如果我们想输出结果是belong的话就太好啦,怎么办呢?暗箱把蚂蚁和窝换一下呗
代码:
#include <cmath>#include <cstdio>#include <cstring>#include <iostream>#define INF 1e9using namespace std;const double eps=1e-9;struct hh{int x,y;}ren[105],hou[105];int n,m,vis[205],belong[105],ans[105];double e[105][105],delta[105],lx[105],ly[105];bool find(int i,int id){ vis[i]=id; for (int j=1;j<=n;j++) if (vis[j+n]!=id) { if ((lx[i]+ly[j]-e[i][j])<=eps && (lx[i]+ly[j]-e[i][j])>=-eps) { vis[j+n]=id; if (!belong[j] || find(belong[j],id)) { belong[j]=i; return 1; } }else delta[j]=min(delta[j],lx[i]+ly[j]-e[i][j]); } return 0;}void KM(){ int i,j,id=0,ii; memset(lx,0x8f,sizeof(lx)); for (i=1;i<=n;i++) for (j=1;j<=n;j++) lx[i]=max(lx[i],e[i][j]); for (ii=1;ii<=n;ii++) { for (j=1;j<=n;j++) delta[j]=INF; while (1) { id++; if (find(ii,id)) break; double a=INF; for (i=1;i<=n;i++) if (vis[i+n]!=id) a=min(delta[i],a); for (i=1;i<=n;i++) { if (vis[i]==id) lx[i]-=a; if (vis[i+n]==id) ly[i]+=a; else delta[i]-=a; } } }}int main(){ int i,j; scanf("%d",&n); for (i=1;i<=n;i++) scanf("%d%d",&hou[i].x,&hou[i].y); for (i=1;i<=n;i++) scanf("%d%d",&ren[i].x,&ren[i].y); for (i=1;i<=n;i++) for (j=1;j<=n;j++) e[i][j]=-sqrt((double)(ren[i].x-hou[j].x)*(ren[i].x-hou[j].x)+(double)(ren[i].y-hou[j].y)*(ren[i].y-hou[j].y)); KM(); for (i=1;i<=n;i++) printf("%d\n",belong[i]);}
阅读全文
1 0
- KM小练
- km算法题目 小做
- vc 小练
- iptables 小练
- C++小练
- js 函数(小练)
- MVC 小练
- hdu1242-搜索小练
- 小练1
- 小练2
- 编辑距离小练
- C++模板小练
- for语句小练
- Python小练
- 排序小练
- 初学者的小练
- 模拟小练
- js小练
- JACK_C#_for循环
- 大数据时代的图表可视化利器——highcharts,D3和百度的echarts
- background-color和background-image一起用的问题
- 值得学习的C语言开源项目
- pandas 学习(一)
- KM小练
- 2017第十届中国国际线缆工业展览会会刊(参展商名录)
- WebView加载网页不显示图片解决办法
- Ubuntu 16.04 + Nvidia 显卡驱动 + Cuda 8.0 (问题总结 + 解决方案)
- 216.Combination Sum III
- zabbix的使用流程原理图
- Spring Boot 拦截器
- 解决:eclipse 断点调试进入到class文件,无法查看变量值问题
- vue+axios自己踩过的坑