Matrix HDU - 2686 [最大流最大費用]
阿新 • • 發佈:2018-11-10
題意:一個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; }