题意:
给你一个n*n的矩阵,然后(0,0)为源点,(n - 1,n - 1)为汇点。点(i,j)可以到达(i + 1,j) (i,j + 1) 花费为a[i][j]。求从(0,0)到(n - 1,n - 1)的最小割。
思路:
如果按照正常建图,Dinic求最大流会超时,这里要充分利用平面图的性质。然后将其转化为对偶图,然后将求最大流最小割转化为求该图的对偶图最短路问题。
详细请参考:http://wenku.baidu.com/view/5a7df375a417866fb84a8e54.html
#include <iostream> #include <cstdio> #include <cmath> #include <vector> #include <cstring> #include <algorithm> #include <string> #include <set> #include <functional> #include <numeric> #include <sstream> #include <stack> #include <map> #include <queue> #define CL(arr, val) memset(arr, val, sizeof(arr)) #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define L(x) (x) << 1 #define R(x) (x) << 1 | 1 #define MID(l, r) (l + r) >> 1 #define Min(x, y) (x) < (y) ? (x) : (y) #define Max(x, y) (x) < (y) ? (y) : (x) #define E(x) (1 << (x)) #define iabs(x) (x) < 0 ? -(x) : (x) #define OUT(x) printf("%I64d\n", x) #define lowbit(x) (x)&(-x) #define Read() freopen("din.txt", "r", stdin) #define Write() freopen("dout.txt", "w", stdout) #define M 1000007 #define N 407 #define ll __int64 using namespace std; const double eps = 1e-10; const int inf = 0x7f7f7f7f; struct node { int v; int w; int next; }g[M]; int head[N*N],ct; struct pnd { int u; int ds; pnd() {} pnd(int x,int y) : u(x),ds(y){} bool operator < (const pnd &a) const { return ds > a.ds; } }; int a[N][N]; int n; int dis[N*N]; bool vt[N*N]; void add(int u,int v,int w) { g[ct].v = v; g[ct].w = w; g[ct].next = head[u]; head[u] = ct++; g[ct].v = u; g[ct].w = w; g[ct].next = head[v]; head[v] = ct++; } //int spfa(int s,int e) //{ // int i; // for (i = 0; i <= e; ++i) // { // dis[i] = inf; // vt[i] = false; // } // dis[s] = 0; // vt[s] = true; // queue<int> Q; // Q.push(s); // while (!Q.empty()) // { // int u = Q.front(); Q.pop(); // for (i = head[u]; i != -1; i = g[i].next) // { // int v = g[i].v; // if (dis[v] > dis[u] + g[i].w) // { // dis[v] = dis[u] + g[i].w; // if (!vt[v]) // { // vt[v] = true; // Q.push(v); // } // } // } // vt[u] = false; // } // // return dis[e]; //} int Dijkstra(int s,int e) { priority_queue<pnd> pQ; for (int i = 0; i <= e; ++i) { dis[i] = inf; vt[i] = false; } dis[s] = 0; pQ.push(pnd(s,dis[s])); while (!pQ.empty()) { pnd ui = pQ.top(); pQ.pop(); if (vt[ui.u]) continue; vt[ui.u] = true; for (int i = head[ui.u]; i != -1; i = g[i].next) { int v = g[i].v; if (dis[v] > ui.ds + g[i].w) { dis[v] = ui.ds + g[i].w; pQ.push(pnd(v,dis[v])); } } } return dis[e]; } int main() { // Read(); int T,i,j; scanf("%d",&T); while (T--) { scanf("%d",&n); for (i = 0; i < n; ++i) for (j = 0; j < n; ++j) scanf("%d",&a[i][j]); CL(head,-1); ct = 0; int s = (n - 1)*(n - 1); int e = s + 1; int x; for (i = 0; i < n; ++i) { for (j = 0; j < n; ++j) { if (i == 0 && j != n - 1) { x = j; add(s,x,a[i][j]); } if (j == n - 1 && i != n - 1) { x = i*(n - 1) + (n - 2); add(s,x,a[i][j]); } if (j == 0 && i != n - 1) { x = i*(n - 1); add(x,e,a[i][j]); } if (i == n - 1 && j != n - 1) { x = (n - 2)*(n - 1) + j; add(x,e,a[i][j]); } if (i != n - 1 && j != n - 1) { if (i) { x = (i - 1)*(n - 1) + j; add((i*(n - 1) + j),x,a[i][j]); } if (j) { x = i*(n - 1) + j - 1; add((i*(n - 1) + j),x,a[i][j]); } } } } //优先队列优化的Dijkstra比spfa快多了。而且spfa时如果s,e链接边的只能是单向,否则会超时 // printf("<<OL<L\n"); // printf("%d\n",spfa(s,e)); printf("%d\n",Dijkstra(s,e)); } return 0; }