1、题目:
HDU1853 Cyclic Tour
题意:
给出一个n个点m条边的有向图,每一条边有边权,从这个图中选出若干个环,使每一个点属于且仅属于一个环,并且使环的权值和最小。
题解:
看到这个东西要想到拆点,然后就用KM求个最大带权匹配就好了,因为要求最小值,就把所有的变成负数求最大值再倒过来
注意如果发现一组不能匹配就直接返回,不要继续累加,不然容易爆int而变成一个极大的负数
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#define INF 1e9
using 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 1e9
using 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 1e9
using 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]);
}