網路流 方格取數+最小割
阿新 • • 發佈:2020-11-17
P2774 方格取數問題
P2774 方格取數問題(https://www.luogu.com.cn/problem/P2774)
題目描述
有一個 mm 行 nn 列的方格圖,每個方格中都有一個正整數。現要從方格中取數,使任意兩個數所在方格沒有公共邊,且取出的數的總和最大,請求出最大的和。
輸入格式
第一行是兩個用空格隔開的整數,分別代表方格圖的行數 mm 和列數 nn。
第 2 到第 (m + 1)行,每行 n 個整數,第 (i + 1)行的第 j個整數代表方格圖第 i 行第 j 列的的方格中的數字 a{i,j} 。
輸出格式
輸出一行一個整數,代表和最大是多少。
輸入輸出樣例
輸入
3 3
1 2 3
3 2 3
2 3 1
輸出
11
說明/提示
資料規模與約定
對於 100% 的資料,保證 1≤n,m≤100,1≤a{i,j}≤10^5。
提示
請注意輸入的第一行先讀入 m 再讀入 n。
思路
不難發現,每個方格會與其上下左右四個方格產生矛盾。程式設計的任務即找到一種不產生矛盾的選擇方案,並且使得取出的數總和最大。
首先對圖進行黑白染色,目的是使產生矛盾的兩個位置分別位於不同的色塊中,方便建圖。
源點與所有白色位置相連,權值為該位置上的數字;所有黑色位置與匯點相連,權值也為該位置上的數字;所有白色位置與其上下左右(注意邊界情況)的黑色位置相連,權值為無窮大。
如此建圖後,可以發現存在源點到匯點的增廣路,這也意味著原圖中存在產生矛盾的兩個位置。假設一開始選取M*N網格中的所有方塊,我們的任務是割掉網路中的一些邊(即刪去一些方塊),使得割去的邊權最小。割去網路中的邊就相當於刪掉兩個矛盾位置中的其中一個,因此當網路中不再有源點到匯點的增廣路,就意味著矛盾全部消除。
問題便轉化為求解最小割(最大流)的問題。輸出答案為全域性和減去最小割。
#include<bits/stdc++.h> using namespace std; const int maxn = 4000 + 10; const int INF = 0x3f3f3f3f; //註釋為弧優化 struct node { int form, to, cap, flow, next; } edge[2000006]; int head[maxn]; int cnt; struct max_Folw { int d[maxn], cur[maxn], start, tend; bool vis[maxn]; void init(int s, int t) { memset(head, -1, sizeof(head)); cnt=0; start=s, tend=t; } void add(int start, int to, int cap) { edge[cnt].form = start; edge[cnt].to = to; edge[cnt].cap = cap; edge[cnt].flow = 0; edge[cnt].next = head[start]; head[start] = cnt++; } void AddEdge(int start, int to, int cap) { add(start, to, cap); add(to, start, 0); } bool BFS() { memset(d, -1, sizeof(d)); int Q[maxn * 2]; int Thead, Ttail; Thead = Ttail = 0; Q[Ttail++] = tend; d[tend] = 0; while (Thead<Ttail) { int x = Q[Thead]; if (x == start) return true; for (int i = head[x]; i != -1; i = edge[i].next) { int temp = edge[i].to; if (d[temp] == -1 && edge[i^1].cap > edge[i^1].flow) { //沒有標記,且可行流大於0 d[temp] = d[x] + 1; Q[Ttail++] = temp; } } Thead++; } return false;//匯點是否成功標號,也就是說是否找到增廣路 } int DFS(int x, int cap) { if (x == tend) return cap; int flow = 0, f; //for (int i = cur[x]; i != -1; i = edge[cur[x]=i].next) { for (int i = head[x]; i != -1; i = edge[i].next) { int temp = edge[i].to; if (d[temp] == d[x] - 1 && edge[i].cap > edge[i].flow) { f = DFS(temp, min(cap - flow, edge[i].cap - edge[i].flow)); edge[i].flow += f; edge[i ^ 1].flow -= f; flow += f; if (flow == cap) return flow; } } d[x] = -2;//防止重搜 return flow; } int maxflow() { int flow = 0, f; while (BFS()) { //memcpy(cur, head, sizeof head); flow += DFS(start, INF); } return flow; } } flow; int a[105][105]; int xx[4]={0, 0, 1, -1}; int yy[4]={1, -1, 0, 0}; int n, m; void Add(int i, int j){ for(int k=0; k<4; k++){ int x=xx[k]+i, y=yy[k]+j; if(x<=n&&x>=1&&y>=1&&y<=m){ flow.AddEdge((i-1)*m+j, (x-1)*m+y, INF); } } } int main(){ scanf("%d%d", &n, &m); int pos0=0, pos1=0; flow.init(0, n*m+1); int ans=0; for(int i=1; i<=n; i++){ for(int j=1; j<=m; j++){ int x; scanf("%d", &a[i][j]); ans+=a[i][j]; if((i+j)%2){ flow.AddEdge(0, (i-1)*m+j, a[i][j]); } else{ flow.AddEdge((i-1)*m+j, n*m+1, a[i][j]); } } } for(int i=1; i<=n; i++){ for(int j=1; j<=m; j++){ if((i+j)%2){ Add(i, j); } } } printf("%d\n", ans-flow.maxflow()); return 0; }