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]);}
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 员工提出辞职后即不来上班怎么办 打架把对方打成轻伤害怎么办 被刑拘了家人该怎么办 看守所在押人员检查出乙肝怎么办 孩子不写作业老师该怎么办 孩子老师向我表白该怎么办 幼儿园老师打孩子家长该怎么办 老师对孩子太苛刻该怎么办 孩子不敢上学怕老师该怎么办 学生上课迟到老师该怎么办 看守所转监狱不收怎么办 己判实刑有病看守所不收怎么办 判决书生效前看守所不收怎么办 法院判实刑看守所不收怎么办 派出所立案后送拘留所了怎么办 打架斗殴被关在看守所了怎么办 在看守所羁押期间患癌症怎么办 无法偿还借款拘留15天后怎么办 离婚起诉被告人被羁押怎么办 事实不清的案件怎么办 交通事故没时间去做笔录怎么办 偷东西被拘留家长该怎么办 在香港被拘留了怎么办 发票认证机卡了怎么办 交罚款的单子丢了怎么办 父亲行政拘留考警察政审不过怎么办 非法经营罪立案后不批刑拘怎么办 12分扣完了怎么办2018 驾驶证c1扣14分怎么办 车辆被扣12分怎么办 两个违章扣12分怎么办 车辆违章扣12分怎么办 一下扣了20分怎么办 违章扣了100多分怎么办 车子累计扣12分怎么办 起诉了对方不来怎么办 在监狱里被打伤了怎么办 初三要体检没去怎么办 羁押人在看守所没判刑怎么办? 在看守所关两年了还没有判刑怎么办 开麻将馆被拘留怎么办