POJ 2175 Evacuation Plan 最小費用流 消負圈

解題思路:最開始一看標準的最小費用流的思路。

求出最小費用,然後和原先的答案進行比較。

不過這鐵定會超時的,所以就死心吧...


接着從題目入手,

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 1e9
const 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;
}





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章