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 }

 

 

你可能感兴趣的:(poj)