codevs 1227 方格取數 2(最小費用最大流)

題目描述 Description

給出一個n*n的矩陣,每一格有一個非負整數Aij,(Aij <= 1000)現在從(1,1)出發,可以往右或者往下走,最後到達(n,n),每達到一格,把該格子的數取出來,該格子的數就變成0,這樣一共走K次,現在要求K次所達到的方格的數的和最大

輸入描述 Input Description

第一行兩個數n,k(1<=n<=50, 0<=k<=10)

接下來n行,每行n個數,分別表示矩陣的每個格子的數

輸出描述 Output Description

一個數,爲最大和

樣例輸入 Sample Input

3 1

1 2 3

0 2 1

1 4 2

樣例輸出 Sample Output

11

數據範圍及提示 Data Size & Hint

1<=n<=50, 0<=k<=10


題解:



本題是POJ 3422 卡卡的矩陣之旅 的弱化版,同時也是《圖論算法理論、實現及應用》中的例題。原題有多組數據。

本題可以轉化爲最小費用最大流問題。

構建容量網絡的方法如下:將每個位置拆成兩個——出點和入點,出點和入點之間連接一條容量爲1、費用爲矩陣中該位置上的數值;若點p與q能連通,則連接p與q,n*n+p與n*n+q,p與n*n+q,n*n+p與q四條邊,容量爲無窮大,費用爲 0;假設源點與匯點,源點與1、匯點與 2*n*n 之間連接邊,容量爲K,用爲0。容量網絡構建完成後,求最小費用最大流即可。


code:


#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;

#define INF 0x3f3f3f3f
#define MIN(a,b) ((a)<(b)?(a):(b))

int mat[55][55];
int n,k;

int head[5005];
int adj[25000];
int next[25000];
int point[25000];
int cost[25000];
int cap[25000];

int pre[5005];
int dis[5005];
bool fl[5005];

int max_flow;
int min_cost;

int edge_cnt;

void add(int u,int v,int cst,int cp)
{
    next[edge_cnt]=head[u];
    head[u]=edge_cnt;
    point[edge_cnt]=v;
    adj[edge_cnt]=u;
    cost[edge_cnt]=cst;
    cap[edge_cnt]=cp;
}

void cost_flow(int src,int tar)
{
    while(1)
    {
        memset(pre,-1,sizeof(pre));
        memset(dis,0x3f,sizeof(dis));
        memset(fl,0,sizeof(fl));

        queue<int>q;
        q.push(src);
        dis[src]=0;

        while(!q.empty())
        {
            int u=q.front();
            q.pop();
            fl[u]=0;

            for(int e=head[u];e!=-1;e=next[e])
            {
                if(cap[e]>0&&dis[u]+cost[e]<dis[point[e]])
                {
                    dis[point[e]]=dis[u]+cost[e];
                    pre[point[e]]=e;
                    if(!fl[point[e]])
                    {
                        fl[point[e]]=1;
                        q.push(point[e]);
                    }
                }
            }
        }

        if(pre[tar]==-1)break;

        int min=INF;

        for(int i=tar;pre[i]!=-1;i=adj[pre[i]])
        {
            min=MIN(min,cap[pre[i]]);
        }
        for(int i=tar;pre[i]!=-1;i=adj[pre[i]])
        {
            cap[pre[i]]-=min;
			//反邊,反悔的機會
            if(pre[i]&1)cap[pre[i]+1]+=min;
            else cap[pre[i]-1]+=min;
        }

        max_flow+=min;
        min_cost+=min*dis[tar];
    }
}

int main()
{
    while(~scanf("%d%d",&n,&k))
    {
        max_flow=0;
        min_cost=0;

        memset(head,-1,sizeof(head));

        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                scanf("%d",&mat[i][j]);
            }
        }

        edge_cnt=0;

        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                int p3=(i-1)*n+j;
                int p4=(i-1)*n+j+n*n;

                edge_cnt++;
                add(p3,p4,-mat[i][j],1);//負數,所以求的是最小費用,答案添負號就行了
                edge_cnt++;
                add(p4,p3,mat[i][j],1);

                edge_cnt++;
                add(p3,p4,0,k);
                edge_cnt++;
                add(p4,p3,0,0);

                if(i<n)
                {
                    int p1=(i-1)*n+j;
                    int p2=i*n+j;

                    //edge_cnt++;
                    //add(p1,p2,0,INF);

                    //edge_cnt++;
                    //add(p1,p2+n*n,0,INF);

                    edge_cnt++;
                    add(p1+n*n,p2,0,k);
                    edge_cnt++;
                    add(p2,p1+n*n,0,0);

                    //edge_cnt++;
                    //add(p1+n*n,p2+n*n,0,INF);
                }
                if(j<n)
                {
                    int p1=(i-1)*n+j;
                    int p2=(i-1)*n+j+1;

                    //edge_cnt++;
                    //add(p1,p2,0,INF);

                    //edge_cnt++;
                    //add(p1,p2+n*n,0,INF);

                    edge_cnt++;
                    add(p1+n*n,p2,0,k);
                    edge_cnt++;
                    add(p2,p1+n*n,0,0);

                    //edge_cnt++;
                    //add(p1+n*n,p2+n*n,0,INF);
                }
            }
        }

        edge_cnt++;
        add(0,1,0,k);
        edge_cnt++;
        add(1,0,0,0);

        edge_cnt++;
        add(n*n*2,n*n*2+1,0,k);
        edge_cnt++;
        add(n*n*2+1,n*n*2,0,0);

        cost_flow(0,2*n*n+1);

        printf("%d\n",-min_cost);
    }
    return 0;
}


本題是POJ 3422 卡卡的矩陣之旅 的弱化版,同時也是《圖論算法理論、實現及應用》中的例題。原題有多組數據。

本題可以轉化爲最小費用最大流問題。

構建容量網絡的方法如下:將每個位置拆成兩個——出點和入點,出點和入點之間連接一條容量爲1、費用爲矩陣中該位置上的數值;若點p與q能連通,則連接p與q,n*n+p與n*n+q,p與n*n+q,n*n+p與q四條邊,容量爲無窮大,費用爲 0;假設源點與匯點,源點與1、匯點與 2*n*n 之間連接邊,容量爲K,用爲0。容量網絡構建完成後,求最小費用最大流即可。

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