POJ 2175 Evacuation Plan 最小费用流 消负圈

来源:互联网 发布:阿里云域名绑定服务器 编辑:程序博客网 时间:2024/05/21 17:52

解题思路:最开始一看标准的最小费用流的思路。

求出最小费用,然后和原先的答案进行比较。

不过这铁定会超时的,所以就死心吧...


接着从题目入手,

Output的最后一句,Your plan need not be optimal itself, but must be valid and better than The City Council's one.

挺阴的...

题目不要求给出最优解,只要一个比先前方案更优的解法就好。

如果把之前的给的方案看成费用流的方案之一的话,

我们只用对那个方案进行一下优化就行。


如果当前流是最优方案时,

当且仅当在残留网络中没有负圈。(后悔边也算在图中)

不然按该负圈回退一次流量,得到的方案的费用一定比原先更优,

且权值等于原先方案的权值加上该负圈的权值。


这样就把原先网络流的问题转化为找负圈的问题,

SPFA即可,只不过在每个点多记录了一个前驱边,在回退流量的时候用。

不过中间有个小bug,在SPFA的过程中,

当访问次数大于一定值时( 这题设为m就足够了 ) 返回的那个点不一定在负圈上,

也有可能是负圈上的点能够遍历的点,

这时就由该点处理一次才能找到一个负圈上的点,

然后再进行回退流量。


#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#include <queue>#include <stack>using namespace std;#define FOR(i,l,r) for(int i=(l);i<=(r);++i)#define REP(i,n) for(int i=0;i<(n);++i)#define DSC(i,r,l) for(int i=(r);i>=(l);--i)#define INF 1e9const int MAXN=210;const int MAXM=100010;struct{    int x,y,c;} a[110],b[110];struct node{    int to;    int next;    int cost;    int flow;} edge[MAXM];int head[MAXN],ip;int que[MAXN*MAXN];int tempc[MAXN];int dis[MAXN];int pre[MAXN];int out[MAXN];int map[110][110];bool visit[MAXN],flag[MAXN];void add(int u,int v,int f,int c){    edge[ip].to=v;    edge[ip].flow=f;    edge[ip].cost=c;    edge[ip].next=head[u];    head[u]=ip++;}int m;int spfa(int start,int numpoint){    FOR(i,1,numpoint) dis[i]=INF;    memset(visit,0,sizeof(visit));    memset(pre,-1,sizeof(pre));    memset(out,0,sizeof(out));    dis[start]=0;    visit[start]=1;    int l=-1,r=-1,top,to,temp;    int mod=numpoint;    que[++r]=start;    while(l!=r)    {        top=que[++l];        visit[top]=0;        for(int p=head[top]; p!=-1; p=edge[p].next)        {            if(!edge[p].flow) continue;            to=edge[p].to;            temp=dis[top]+edge[p].cost;            if(dis[to]>temp)            {                dis[to]=temp;                pre[to]=p^1;                if(!visit[to])                {                    if(++out[to]>m+1) return to;                    visit[to]=1;                    que[++r]=to;                }            }        }    }    return -1;}bool solve(int n,int m){    memset(flag,0,sizeof(flag));    int start=0;    FOR(i,1,m) add(start,i+n,1,0);    int temp=spfa(start,n+m+1);    if(temp!=-1)    {        while(!flag[temp])//最初找到的这个点不一定在负圈上,要另加处理        {            flag[temp]=1;            temp=edge[ pre[temp] ].to;        }        int x=temp;        edge[ pre[x] ].flow++;  //回退流量        edge[ pre[x]^1 ].flow--;//回退流量        x=edge[ pre[x] ].to;        while(x!=temp)        {            edge[ pre[x] ].flow++;  //回退流量            edge[ pre[x]^1 ].flow--;//回退流量            x=edge[ pre[x] ].to;        }        return 1;    }    return 0;}int main(){    int n,x;    //freopen("in.txt","r",stdin);    while(cin>>n>>m)    {        memset(head,-1,sizeof(head));   ip=0;        memset(tempc,0,sizeof(tempc)); //记录某个避难所已经使用了多少容量        FOR(i,1,n)  scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].c);        FOR(i,1,m)  scanf("%d%d%d",&b[i].x,&b[i].y,&b[i].c);        FOR(i,1,n)            FOR(j,1,m)                map[i][j]=abs(a[i].x-b[j].x)+abs(a[i].y-b[j].y)+1;        FOR(i,1,n)            FOR(j,1,m)            {                scanf("%d",&x);                add(i,j+n,INF,map[i][j]);                add(j+n,i,x ,-map[i][j]);//后悔边                tempc[j]+=x;            }        int end=n+m+1;        FOR(i,1,m)        {            add(i+n,end,b[i].c-tempc[i],0);            add(end,i+n,tempc[i],0);//后悔边        }        if(!solve(n,m)) puts("OPTIMAL");        else        {            puts("SUBOPTIMAL");            int temp=1;            FOR(i,1,n)            {                FOR(j,1,m)                {                    if(j>1) putchar(' ');                    printf("%d",edge[temp].flow);                    temp+=2;                }                printf("\n");            }        }    }    return 0;}





原创粉丝点击