1. 程式人生 > >Matrix HDU - 2686 [最大流最大費用]

Matrix HDU - 2686 [最大流最大費用]

Matrix HDU - 2686

題意:一個n*n(n<=30)的矩陣,求從(1,1)出發到(n,n)的兩條路徑,滿足除了起點和終點之外,兩條路徑不得有重複.求最大和

思路:

  • MAXN開小了一直TLE
  • 對於每一個點,可以接一條邊到下方和右方
  • 然而對於一個點,只能選擇2次.則有流經該點的流<=1
  • 但是原圖不能直接構造
  • 於是對每一個節點我們開影子節點(拆點),容量為1,這樣就可以限流(限制次數了)
  • 注意一下最大流的時候代價取反,對答案再取絕對值即可
#include <cstdio>
#include <cstring>
#include <queue>
#include <stack>
#include <vector>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <iostream>
#define mAXn 2000+10
#define mAXm 30000+30
#define InF 0x3f3f3f3f
using namespace std;
struct Edge
{
    int from, to, cap, flow, cost, next;
};
Edge edge[mAXm];
int head[mAXn], edgenum;
int pre[mAXn];//記錄增廣路徑上 到達點i的邊的編號
int dist[mAXn];
bool vis[mAXn];
int n, m;//點數 邊數
int source, sink;//超級源點 超級匯點
void init()
{
    edgenum = 0;
    memset(head, -1, sizeof(head));
}
void addEdge(int u, int v, int w, int c)
{
    Edge E1 = {u, v, w, 0, c, head[u]};
    edge[edgenum] = E1;
    head[u] = edgenum++;
    Edge E2 = {v, u, 0, 0, -c, head[v]};
    edge[edgenum] = E2;
    head[v] = edgenum++;
}
bool SPFA(int s, int t)//尋找花銷最少的路徑
{
    //跑一遍SPFA 找s——t的最少花銷路徑 且該路徑上每一條邊不能滿流
    //若存在 說明可以繼續增廣,反之不能
    queue<int> Q;
    memset(dist, InF, sizeof(dist));
    memset(vis, false, sizeof(vis));
    memset(pre, -1, sizeof(pre));
    dist[s] = 0;
    vis[s] = true;
    Q.push(s);
    while(!Q.empty())
    {
        int u = Q.front();
        Q.pop();
        vis[u] = false;
        for(int i = head[u]; i != -1; i = edge[i].next)
        {
            Edge E = edge[i];
            if(dist[E.to] > dist[u] + E.cost && E.cap > E.flow)//可以鬆弛 且 沒有滿流
            {
                dist[E.to] = dist[u] + E.cost;
                pre[E.to] = i;//記錄前驅邊 的編號
                if(!vis[E.to])
                {
                    vis[E.to] = true;
                    Q.push(E.to);
                }
            }
        }
    }
    return pre[t] != -1;//可達返回true
}
void mCmF(int s, int t, int &cost, int &flow)
{
    flow = 0;//總流量
    cost = 0;//總費用
    while(SPFA(s, t))//每次尋找花銷最小的路徑
    {
        int Min = InF;
        //通過反向弧 在源點到匯點的最少花費路徑 找最小增廣流
        for(int i = pre[t]; i != -1; i = pre[edge[i^1].to])
        {
            Edge E = edge[i];
            Min = min(Min, E.cap - E.flow);
        }
        //增廣
        for(int i = pre[t]; i != -1; i = pre[edge[i^1].to])
        {
            edge[i].flow += Min;
            edge[i^1].flow -= Min;
            cost += edge[i].cost * Min;//增廣流的花銷
        }
        flow += Min;//總流量累加
    }
}
int k;
int a[50][50];
int hs(int i,int j){
    return (i-1)*n+j;
}
bool check(int i,int j){
    if(i<=n&&j<=n)  return true;
    else    return false;
}
void getmap(){
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)   scanf("%d",&a[i][j]);
    source=0,sink=1+2*n*n;
    addEdge(0,1,2,0);
    addEdge(hs(1,1),hs(1,2),1,-a[1][2]);
    addEdge(hs(1,1),hs(2,1),1,-a[2][1]);
    addEdge(n*n,sink,2,0);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(i==1&&j==1)  continue;
            if(i==n&&j==n)  break;
            int id=hs(i,j)+n*n;
            addEdge(hs(i,j),id,1,0);
            if(check(i,j+1))   addEdge(id,hs(i,j+1),1,-a[i][j+1]);
            if(check(i+1,j))   addEdge(id,hs(i+1,j),1,-a[i+1][j]);
        }
    }
    return ;
}

int main()
{
    while(scanf("%d", &n)==1)
    {
        init();
        getmap();//建圖
        int cost, flow;//最小費用 最大流
        mCmF(source, sink, cost, flow);
        if(flow<k)  printf("-1\n");
        else    printf("%d\n", -cost-a[n][n]+a[1][1]);//最小費用 最大流
//        printf("%d\n", flow);//最小費用 最大流
    }
    return 0;
}