消圈定理:残留网络里如果存在负费用圈,那么当前流不是最小费用流。因为你绕着这个圈走一遍,费用更小了。
这道题是给你图之后,再给你一个分配方法,让你判断这个方法是不是最优的。
如果我们直接用走费用流的话会超时的,我们需要在他给的那个分配方法的基础上来做。
我们知道费用流问题是有反向边代表已经花费了的容量啥的,我们普通的建边时是设为0的,这次我们不设为0,
而是按照他给的方案来设置。就是说我们是从半途中开始搜寻最小费用流的。这样就不会超时了。
如果建好图,这个图存在负圈,也就是说明这不是最优的,因为我们走一遍负圈,我们的费用会减少。
那么i->j ++, j->i --。 最后我们输出我们的最优分配方法即可。
学习地址:
http://blog.csdn.net/u013761036/article/details/46363631
http://blog.csdn.net/onepiecehuiyu/article/details/8139377
ss -> i 流量0 费用0
//因为跑完之后前面肯定是流量都用没了
i -> ss 流量c ,费用0
//c是这个建筑有多少人,满流的正向0,反向满c
i -> j + n 流量INF-c 费用 w
//w是i,j的距离+1,c是建筑里人数,本来是INF,跑完后是INF-c
j+n -> i 流量c ,费用w
//如上
j+n -> tt 流量q,费用0,
//q是建筑的容量剩余,就是所有的-当前用了的,当前用的综合自己算出来
tt -> j+n 流量p,费用0
//p是当前这个避难所一共用了多少容量
- #include<queue>
- #include<stdio.h>
- #include<string.h>
- #define N_node 205
- #define N_edge 30000
- #define INF 100000000
- using namespace std;
- typedef struct
- {
- int from ,to ,cost ,flow ,next;
- }STAR;
- typedef struct
- {
- int a ,b ,c;
- }NODE;
- STAR E[N_edge];
- int list[N_node] ,tot;
- int C[N_node];//入队次数
- int mer[N_node];//记录路径
- int s_x[N_node] ,mark[N_node];
- int now[N_node][N_node];
- NODE A[N_node] ,B[N_node];
- void add(int a ,int b ,int c ,int d)
- {
- E[++tot].from = a;
- E[tot].to = b;
- E[tot].cost = c;
- E[tot].flow = d;
- E[tot].next = list[a];
- list[a] = tot;
- }
- int abss(int x)
- {
- return x > 0 ? x : -x;
- }
- bool Spfa(int s ,int n)
- {
- for(int i = 0 ;i <= n ;i ++)
- s_x[i] = INF;
- memset(mark ,0 ,sizeof(mark));
- memset(C ,0 ,sizeof(C));
- queue<int>q; q.push(s);
- mark[s] = C[s] = 1 ,s_x[s] = 0;
- int xin ,tou;
- memset(mer ,255 ,sizeof(mer));
- while(!q.empty())
- {
- tou = q.front();
- q.pop();
- mark[tou] = 0;
- for(int k = list[tou] ;k ;k = E[k].next)
- {
- xin = E[k].to;
- if(s_x[xin] > s_x[tou] + E[k].cost && E[k].flow)
- {
- s_x[xin] = s_x[tou] + E[k].cost;
- mer[xin] = k;
- if(!mark[xin])
- {
- mark[xin] = 1;
- q.push(xin);
- if(++C[xin] > n) return xin;
- }
- }
- }
- }
- return 0;
- }
- int main ()
- {
- int n ,m ,i ,j;
- int st[N_node];
- while(~scanf("%d %d" ,&n ,&m))
- {
- for(i = 1 ;i <= n ;i++)
- scanf("%d %d %d" ,&A[i].a ,&A[i].b ,&A[i].c);
- for(i = 1 ;i <= m ;i ++)
- scanf("%d %d %d" ,&B[i].a ,&B[i].b ,&B[i].c);
- memset(st ,0 ,sizeof(st));
- for(i = 1 ;i <= n ;i ++)
- for(j = 1 ;j <= m ;j ++)
- {
- scanf("%d" ,&now[i][j]);
- st[j] += now[i][j];
- }
- memset(list ,0 ,sizeof(list));
- tot = 1;
- int ss = 0 ,tt = n + m + 1;
- for(i = 1 ;i <= n ;i ++)
- add(ss ,i ,0 ,0),add(i ,ss ,0 ,A[i].c);
- for(i = 1 ;i <= m ;i ++)
- add(i + n ,tt ,0 ,B[i].c - st[i]) ,add(tt ,i + n ,0 ,st[i]);
- for(i = 1 ;i <= n ;i ++)
- for(j = 1 ;j <= m ;j ++)
- {
- add(i ,j + n ,abss(A[i].a-B[j].a)+abss(A[i].b - B[j].b) + 1 ,INF - now[i][j]);
- add(j + n ,i ,-(abss(A[i].a-B[j].a)+abss(A[i].b - B[j].b) + 1) ,now[i][j]);
- }
- int x = Spfa(tt ,tt);
- if(!x)
- {
- printf("OPTIMAL\n");
- continue;
- }
- printf("SUBOPTIMAL\n");
- memset(mark ,0 ,sizeof(mark));
- i = mer[x];
- while(1)//找到一个肯定在环上的点
- {
- x = E[i].to;
- if(mark[x]) break;
- mark[x] = 1;
- i = mer[E[i].from];
- }
- memset(mark ,0 ,sizeof(mark));
- for(i = mer[x] ;i + 1 ;i = mer[E[i].from])
- {
- int a = E[i].from ,b = E[i].to;
- if(a >= 1 && a <= n && b >= n + 1 && b <= n + m)
- now[a][b-n] ++;
- if(a >= n + 1 && a <= n + m && b >= 1 && b <= n)
- now[b][a-n] --;
- if(mark[a] && mark[b]) break;
- mark[a] = mark[b] = 1;
- }
- for(i = 1 ;i <= n ;i ++)
- for(j = 1 ;j <= m ;j ++)
- if(j == m) printf("%d\n" ,now[i][j]);
- else printf("%d " ,now[i][j]);
- }
- return 0;
- }