zoukankan      html  css  js  c++  java
  • poj

    http://poj.org/problem?id=2195

    对km算法不理解,模板用的也不好。

    下面是大神的解释。

    KM算法的要点是在相等子图中寻找完备匹配,其正确性的基石是:任何一个匹配的权值之和都不大于所有顶点的顶标之和,而能够取到相等的必然是最大权匹配。
    左右两边点数不等时,KM算法的正确性也是可以得到保证的。原因如下:
    由KM算法中可行点标的定义,有:
    任意匹配的权值 <= 该匹配所覆盖的所有点的顶标值 <= KM算法所得到的匹配所覆盖的所有点的顶标值 = KM算法所得到的的匹配的权值
    
    上面的证明与网上大多数证明不同点在于,网上的证明中只是模糊地说“图中所有点的顶标值”,这只在两点集点数相等时才正确。
    
    上面第二个不等号最为关键,它的理由是:假定|X|<=|Y|,则任意时刻,Y集合中KM算法所取的匹配未覆盖的点的顶标必为0!
      1 #include <cstdio>
      2 #include <cstring>
      3 #include <cmath>
      4 #include <algorithm>
      5 using namespace std;
      6 
      7 const int maxn = 2000 + 10;
      8 
      9 const int INF = 0x7fffffff;
     10 
     11 int n,m;
     12 int W[maxn][maxn];  //存储权值
     13 int Lx[maxn], Ly[maxn];   // 顶标
     14 int left[maxn];          // left[i]为右边第i个点的匹配点编号
     15 bool S[maxn], T[maxn];   // S[i]和T[i]为左/右第i个点是否已标记
     16 
     17 
     18 bool match(int i){
     19   S[i] = true;
     20   for(int j = 1; j <= m; j++) if (Lx[i]+Ly[j]==W[i][j] && !T[j])
     21   {
     22     T[j] = true;
     23     if (!left[j] || match(left[j]))
     24     {
     25       left[j] = i;
     26       return true;
     27     }
     28   }
     29   return false;
     30 }
     31 
     32 void update(){
     33   int a = INF;
     34   for(int i = 1; i <= n; i++) if(S[i])
     35     for(int j = 1; j <= m; j++) if(!T[j])
     36       a = min(a, W[i][j]-Lx[i]-Ly[j]); //若是最大权匹配则Lx[i]+Ly[j]-W[i][j]; 同样是取最小值
     37   for(int i = 1; i <= n; i++) {
     38     if(S[i]) Lx[i] -= a;
     39     if(T[i]) Ly[i] += a;
     40   }
     41 }
     42 
     43 void KM() {
     44   for(int i = 1; i <= n; i++) {
     45     left[i] = Lx[i] = Ly[i] = 0;
     46     for(int j = 1; j <= m; j++)
     47       Lx[i] = min(Lx[i], W[i][j]); //若是最大权匹配.则初始值顶标取最大值,最小匹配则去最小值
     48   }
     49   for(int i = 1; i <= n; i++) {
     50     for(;;) {
     51       for(int j = 1; j <= m; j++) S[j] = T[j] = 0;
     52       if(match(i)) break; else update();
     53     }
     54   }
     55 }
     56 char s[305];
     57 struct point
     58 {
     59     int x,y;
     60 };
     61 point man[305],house[305];
     62 int main(){
     63     //freopen("a.txt","r",stdin);
     64     int sum,man_num,house_num;
     65     while(scanf("%d%d",&n,&m)!=EOF&&(n+m))
     66     {
     67         int i,j;
     68         man_num=1,house_num=1;
     69         for(int i=1;i<=n;i++)
     70         {
     71             scanf("%s",s+1);
     72             for(int j=1;j<=m;j++)
     73             {
     74                 if(s[j]=='m')
     75                 {
     76                     man[man_num].x=i;
     77                     man[man_num++].y=j;
     78                 }
     79                 else if(s[j]=='H')
     80                 {
     81                     house[house_num].x=i;
     82                     house[house_num++].y=j;
     83                 }
     84             }
     85         }
     86         man_num--,house_num--;
     87       //  printf("%d %d
    ",man_num,house_num);
     88         n=man_num;m=house_num;
     89         for(int i=1;i<=man_num;i++)
     90             for(int j=1;j<=house_num;j++)
     91             W[i][j]=abs(man[i].x-house[j].x)+abs(man[i].y-house[j].y);
     92         sum=0;
     93         KM(); // 最大权匹配
     94         for(i=1;i<=m;i++)
     95             sum+=W[left[i]][i];
     96         printf("%d
    ",sum);
     97     }
     98     //for(int i = 1; i <= n; i++) printf("left[%d]=%d
    ", i,left[i]);
     99     //getch();
    100   return 0;
    101 }

    当然还可以用费用流来做.

    算出每一个到每一个房子的距离后建图。
    源点与人连,容量1,费用0
    人与每个房子都要连,容量1,费用为距离
    每个房子与汇点连,容量1,费用0
    求一次最小费用即可
    另外此题数据的范围开大点。
    Bellmanford:
      1 #include<cstdio>
      2 #include<cstring>
      3 #include<queue>
      4 #include<vector>
      5 #include<algorithm>
      6 #include<cassert>
      7 using namespace std;
      8 
      9 const int maxn = 2000 + 20;
     10 const int INF = 1000000000;
     11 
     12 struct Edge 
     13 {
     14     int from, to, cap, flow, cost;
     15     Edge(int u, int v, int c, int f, int w):from(u),to(v),cap(c),flow(f),cost(w) {}
     16 };
     17 
     18 struct MCMF 
     19 {
     20     int n, m;
     21     vector<Edge> edges;
     22     vector<int> G[maxn];
     23     int inq[maxn];         // 是否在队列中
     24     int d[maxn];           // Bellman-Ford
     25     int p[maxn];           // 上一条弧
     26     int a[maxn];           // 可改进量
     27 
     28     void init(int n) 
     29     {
     30         this->n = n;
     31         for(int i = 0; i < n; i++) G[i].clear();
     32         edges.clear();
     33     }
     34 
     35     void AddEdge(int from, int to, int cap, int cost) 
     36     {
     37         edges.push_back(Edge(from, to, cap, 0, cost));
     38         edges.push_back(Edge(to, from, 0, 0, -cost));
     39         m = edges.size();
     40         G[from].push_back(m-2);
     41         G[to].push_back(m-1);
     42     }
     43 
     44     bool BellmanFord(int s, int t, int flow_limit, int& flow, int& cost) 
     45     {
     46         for(int i = 0; i < n; i++) d[i] = INF;
     47         memset(inq, 0, sizeof(inq));
     48         d[s] = 0; inq[s] = 1; p[s] = 0; a[s] = INF;
     49 
     50         queue<int> Q;
     51         Q.push(s);
     52         while(!Q.empty()) 
     53         {
     54           int u = Q.front(); Q.pop();
     55           inq[u] = 0;
     56           for(int i = 0; i < G[u].size(); i++) 
     57           {
     58             Edge& e = edges[G[u][i]];
     59             if(e.cap > e.flow && d[e.to] > d[u] + e.cost) 
     60             {
     61               d[e.to] = d[u] + e.cost;
     62               p[e.to] = G[u][i];
     63               a[e.to] = min(a[u], e.cap - e.flow);
     64               if(!inq[e.to]) { Q.push(e.to); inq[e.to] = 1; }
     65             }
     66           }
     67         }
     68         if(d[t] == INF) return false;
     69         if(flow + a[t] > flow_limit) a[t] = flow_limit - flow;
     70         flow += a[t];
     71         cost += d[t] * a[t];
     72         for(int u = t; u != s; u = edges[p[u]].from) 
     73         {
     74           edges[p[u]].flow += a[t];
     75           edges[p[u]^1].flow -= a[t];
     76         }
     77         return true;
     78       }
     79 
     80       // 需要保证初始网络中没有负权圈
     81       int MincostFlow(int s, int t, int flow_limit, int& cost) 
     82       {
     83         int flow = 0; cost = 0;
     84         while(flow < flow_limit && BellmanFord(s, t, flow_limit, flow, cost));
     85         return flow;
     86       }
     87 
     88 };
     89 
     90 MCMF g;
     91 typedef struct{
     92     int x,y;
     93 }Point;
     94 
     95 Point man[maxn],home[maxn];
     96 int man_sum,home_sum;
     97 
     98 
     99 int main()
    100 {
    101     int n,m,i,j;
    102     char s[maxn];
    103     while(~scanf("%d%d",&n,&m) && n || m)
    104     {
    105         getchar();
    106         man_sum=home_sum=1;
    107         for(i=1;i<=n;i++)
    108         {        //记录下人与房子的坐标
    109             gets(s);
    110             for(j=0;j<m;j++)
    111             {
    112                 if(s[j]=='m')
    113                 {
    114                     man[man_sum].x=i;
    115                     man[man_sum++].y=j+1;
    116                 }
    117                 if(s[j]=='H')
    118                 {
    119                     home[home_sum].x=i;
    120                     home[home_sum++].y=j+1;
    121                 }
    122             }
    123         }
    124         man_sum--; home_sum--;
    125         g.init(man_sum+home_sum+2);
    126 
    127         for(i=1;i<=man_sum;i++) g.AddEdge(0, i, 1, 0);        //源点指向人
    128 
    129         for(i=1;i<=man_sum;i++)  //人指向房子
    130         {                            
    131             for(j=man_sum+1;j<=man_sum+home_sum;j++)
    132             {
    133                 g.AddEdge(i, j, 1, abs(man[i].x-home[j-man_sum].x)+abs(man[i].y-home[j-man_sum].y));
    134             }
    135         }
    136 
    137         for(i=man_sum+1;i<=man_sum+home_sum;i++) g.AddEdge(i, man_sum+home_sum+1, 1, 0);        //房子指向汇点
    138 
    139         int cost;
    140         g.MincostFlow(0, man_sum+home_sum+1, man_sum, cost);
    141         printf("%d
    ", cost);
    142     }
    143     return 0;
    144 }

    SPFA:

    参考:http://blog.csdn.net/lenleaves/article/details/7904588

      1 #include<iostream>
      2 #include<algorithm>
      3 #include<cstring>
      4 #include<string>
      5 #include<queue>
      6 #include<cmath>
      7 #include<cstdio>
      8 
      9 using namespace std;
     10 
     11 #define MAXN 2000
     12 #define MAXM 110000
     13 #define INF 0x3FFFFFF
     14 
     15 struct edge
     16 {
     17     int to,c,w,next;
     18 };
     19 
     20 edge e[MAXM];
     21 bool in[MAXN];
     22 int hx[500],hy[500],mx[500],my[500];
     23 int head[MAXN],dis[MAXN],pre[MAXN],en,maxflow,mincost;
     24 int vn,st,ed,r,c,hn,mn;
     25 
     26 void add(int a,int b,int c,int d)
     27 {
     28     e[en].to=b;
     29     e[en].c=c;
     30     e[en].w=d;
     31     e[en].next=head[a];
     32     head[a]=en++;
     33     e[en].to=a;
     34     e[en].c=0;
     35     e[en].w=-d;
     36     e[en].next=head[b];
     37     head[b]=en++;    
     38 }
     39 
     40 bool spfa(int s)
     41 {
     42     queue<int> q;
     43     for(int i=0;i<=vn;i++)
     44     {
     45         dis[i]=INF;
     46         in[i]=false;
     47         pre[i]=-1;
     48     }
     49     dis[s]=0;
     50     in[s]=true;
     51     q.push(s);
     52     while(!q.empty())
     53     {
     54         int tag=q.front();
     55         in[tag]=false;
     56         q.pop();
     57         for(int i=head[tag];i!=-1;i=e[i].next)
     58         {
     59             int j=e[i].to;
     60             if(e[i].w+dis[tag]<dis[j] && e[i].c)
     61             {
     62                 dis[j]=e[i].w+dis[tag];
     63                 pre[j]=i;
     64                 if(!in[j])
     65                 {
     66                     q.push(j);
     67                     in[j]=true;
     68                 }
     69             }
     70         }
     71     }
     72     if(dis[ed]==INF)
     73         return false;
     74     return true;
     75 }
     76 
     77 void update()  
     78 {  
     79     int flow=INF;  
     80     for (int i=pre[ed];i!=-1;i=pre[e[i^1].to])
     81         if(e[i].c<flow) flow=e[i].c;    
     82     for (int i=pre[ed];i!=-1;i=pre[e[i^1].to]) 
     83     {
     84         e[i].c-=flow,e[i^1].c+=flow;
     85     }   
     86     maxflow+=flow; 
     87     mincost+=flow*dis[ed];
     88 }  
     89 
     90 void mincostmaxflow()
     91 {
     92     maxflow=0,mincost=0;
     93     while(spfa(st))
     94         update();
     95 }
     96 
     97 void solve()
     98 {
     99     char s[500];
    100     hn=0,mn=0,en=0;
    101     memset(head,-1,sizeof(head));
    102     for(int i=1;i<=r;i++)
    103     {
    104         scanf("%s",s);
    105         for(int j=0;j<c;j++)
    106         {
    107             if(s[j]=='m') mx[mn]=i,my[mn++]=j+1;
    108             if(s[j]=='H') hx[hn]=i,hy[hn++]=j+1;
    109         }
    110     }
    111     vn=mn+hn+5;
    112     st=0,ed=mn+hn+1;
    113     for(int i=0;i<mn;i++)
    114         for(int j=0;j<hn;j++)
    115         {
    116             int dist=abs(mx[i]-hx[j])+abs(my[i]-hy[j]);
    117             add(i+1,j+1+mn,1,dist);
    118         }
    119     for(int i=0;i<mn;i++)
    120         add(st,i+1,1,0);
    121     for(int i=0;i<hn;i++)
    122         add(i+1+mn,ed,1,0);
    123     mincostmaxflow();
    124     printf("%d
    ",mincost);
    125 }
    126 
    127 int main()
    128 {
    129     while(scanf("%d%d",&r,&c)!=EOF && r+c)
    130         solve();
    131     return 0;
    132 }
  • 相关阅读:
    文本框小写变大写控制
    SQL2005 递归查询示例,非常方便
    GridView分页后进行添加,删除操作后,仍返回到当前页码
    从ASP.NET 1.1升级到ASP.NET 2.0要考虑的Cookie问题
    英语常用口语
    ASP.NET会话(Session)保存模式
    .NET2005文档自动生成
    JavaScript 弹出窗口总结
    SQL SERVER和SYBASE的渊源
    A versatile HDR Video Production System笔记
  • 原文地址:https://www.cnblogs.com/nowandforever/p/4607903.html
Copyright © 2011-2022 走看看