POJ 2175 费用流(消圈)

题意:

 一个城市有n座建筑物,每个建筑物里面有一些人,为了在战争爆发时这些人都可以避难,城市里面建了m座避难所。每座避难所只能容纳有限人数。给出每个建筑物和避难所的坐标(题目要求距离为曼哈顿距离+1)。现在给你一种避难方案,问这种方案是否为最优方案,如果不是,请输出一种比当前更优的方案(不一定最优)。

答案给出的方案不是最优的,当且仅当存在另一种方案的所有人移动的总路程比答案少。。

 

题解:

建图,费用流超时。。

然后看了下费用流的消圈算法,就是用来验证当前的费用流是否是最小的。

消圈定理:残留网络里如果存在负费用圈,那么当前流不是最小费用流。

负圈有必要解释一下:费用总和是负数,且每条边的剩余流量大于0

 

网上好像都是简化版的消圈,我写的是完整版的,更好理解一些,就是边多了一些,不过不太影响速度

网上的题解都说的那么玄妙,其实就是按照题目给图的答案构造残余网络,然后利用spfa找负圈(负环)判断其是否是最小费用流~

 

ps:还以为是我spfa写错了呢。。。原来边权打翻了。。。

 

View Code
  1 #include <iostream>
  2 #include <cstdlib>
  3 #include <cstring>
  4 #include <cstdio>
  5 #include <algorithm>
  6 #include <cmath>
  7 
  8 #define N 250
  9 #define M 500000
 10 #define INF 1e9
 11 
 12 using namespace std;
 13 
 14 int head[N],next[M],to[M],pr[M],len[M];
 15 int q[N*M],dis[N],pre[N],im[N];
 16 int map[N][N],hx[N],hy[N],hp[N],sx[N],sy[N],sr[N],sum[N];
 17 int rc[N][N];
 18 bool vis[N];
 19 int n,m,cnt,S,T,rt;
 20 
 21 inline void prep()
 22 {
 23     for(int i=1;i<=n;i++)
 24         for(int j=1;j<=m;j++)
 25             map[i][j]=abs(hx[i]-sx[j])+abs(hy[i]-sy[j])+1;
 26 }
 27 
 28 inline void add(int u,int v,int r,int w)
 29 {
 30     to[cnt]=v; len[cnt]=r; pr[cnt]=w; next[cnt]=head[u]; head[u]=cnt++;
 31 }
 32 
 33 inline void read()
 34 {
 35     memset(sum,0,sizeof sum);
 36     memset(head,-1,sizeof head); cnt=0;
 37     for(int i=1;i<=n;i++) scanf("%d%d%d",&hx[i],&hy[i],&hp[i]);
 38     for(int i=1;i<=m;i++) scanf("%d%d%d",&sx[i],&sy[i],&sr[i]);
 39     prep();
 40     for(int i=1,a;i<=n;i++)
 41         for(int j=1;j<=m;j++)
 42         {
 43             scanf("%d",&a); rc[i][j]=a;
 44             add(i,n+j,INF-a,map[i][j]);
 45             add(n+j,i,a,-map[i][j]);
 46             sum[j]+=a;
 47         }
 48     S=0; T=n+m+1;
 49     for(int i=1;i<=n;i++) add(S,i,0,0),add(i,S,hp[i],0);
 50     for(int i=1;i<=m;i++) add(i+n,T,sr[i]-sum[i],0),add(T,i+n,sum[i],0);
 51 }
 52 
 53 inline int spfa()
 54 {
 55     memset(vis,0,sizeof vis);
 56     memset(dis,0x3f,sizeof dis);
 57     memset(im,0,sizeof im);
 58     memset(pre,-1,sizeof pre);
 59     int h=1,t=2,sta;
 60     dis[T]=0; vis[T]=true; q[1]=T; im[T]++;
 61     while(h<t)
 62     {
 63         sta=q[h++]; vis[sta]=false;
 64         for(int i=head[sta];~i;i=next[i])
 65             if(len[i]&&dis[to[i]]>dis[sta]+pr[i])
 66             {
 67                 dis[to[i]]=dis[sta]+pr[i];
 68                 pre[to[i]]=sta;
 69                 if(!vis[to[i]])
 70                 {
 71                     vis[to[i]]=true; q[t++]=to[i]; ++im[to[i]];
 72                     if(im[to[i]]>n+m+2) return to[i];
 73                 }
 74             }
 75     }
 76     return -1;
 77 }
 78 
 79 inline void go()
 80 {
 81     int rt=spfa();
 82     int st;
 83     if(rt==-1) {puts("OPTIMAL");return;}
 84     puts("SUBOPTIMAL"); 
 85     memset(vis,0,sizeof vis);
 86     int sta=rt;
 87     while(true)
 88     {
 89         if(!vis[sta]) vis[sta]=true,sta=pre[sta];
 90         else {rt=sta;break;}
 91     }
 92     do
 93     {
 94         int from=pre[sta],to=sta;
 95         if(from<=n&&to>n) rc[from][to-n]++;
 96         if(to<=n&&from>n) rc[to][from-n]--;
 97         sta=pre[sta];
 98     }while(sta!=rt);
 99     for(int i=1;i<=n;i++)
100     {
101         printf("%d",rc[i][1]);
102         for(int j=2;j<=m;j++)
103             printf(" %d",rc[i][j]);
104         puts("");
105     }
106 }
107 
108 int main()
109 {
110     while(scanf("%d%d",&n,&m)!=EOF) read(),go();
111     return 0;
112 }

 

 

posted @ 2013-01-09 01:25  proverbs  阅读(1309)  评论(2编辑  收藏  举报