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 }
没有人能阻止我前进的步伐,除了我自己!