HDU 1565 方格取數 題解
阿新 • • 發佈:2019-01-22
【題目】:
Problem Description 給你一個n*n的格子的棋盤,每個格子裡面有一個非負數。從中取出若干個數,使得任意的兩個數所在的格子沒有公共邊,就是說所取的數所在的2個格子不能相鄰,並且取出的數的和最大。
Input 包括多個測試例項,每個測試例項包括一個整數n 和n*n個非負數(n<=20)
Output 對於每個測試例項,輸出可能取得的最大的和
Sample Input 3 75 15 21 75 15 28 34 70 5
Sample Output 188
【分析】:
這個問題的模型就是求二分圖點權最大獨立集(二分圖點權最大獨立集即:選 出圖中一些點,使得這些點之間沒有邊相連,且這些點的權值之和最大)。對於這種問題,我們可以將其轉化為最小割模型,從而用最大流演算法解決。(結論:最大點權獨立集
= 所有點權 - 最小點權覆蓋 = 所有點權 - 最小割 = 所有點權 - 最大流)簡單割集為未割容量為無窮的邊的割邊集,且可證,最小割一定是簡單割,又求最小簡單割的建模方法就是把XY集合之間的邊的容量設為無窮大,而二分圖中,最小點權覆蓋值就是最小簡單割。這樣一來,基本構圖就出來了:
首先我們把棋盤黑白染色,目的是讓相鄰的格子的定義顏色(黑和白)不同,我們將所有的黑色格子看做二分圖X集合中的頂點,白色格子看做Y集合頂點,並建立源點S和匯點T。
建圖規則如下:
1、從S向X集合中每個頂點連線一條容量為格子權值的有向邊。
2、從Y集合中每個頂點向T連線一條容量為格子權值的有向邊。
3、相鄰格子Xi,Yj之間從Xi向Yj連線一條容量為無窮大的有向邊。(程式碼實現時注意,此處不要連成了雙向邊)
最後,我們求出該圖的最大流,那麼所要求的結果就是所有格子中數值總和減去最大流。
有關二分圖最大點權獨立集的問題,請見《最小割模型在資訊學競賽中的應用》。
【程式碼】:
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<iostream> #include<algorithm> using namespace std; #define MAXNODE 403 #define MAXE 4003 #define IMAX 214748364 struct EDGE{int f,t,next,flow;}a[MAXE]; int N,tot,last[MAXNODE],ans,total,S,T; int d[MAXNODE],Q[MAXE]; const int dx[4]={0,1,0,-1}; const int dy[4]={1,0,-1,0}; void add(int from,int to,int flow) { a[tot].t=to; a[tot].flow=flow; a[tot].next=last[from]; last[from]=tot++; a[tot].t=from; a[tot].flow=0; a[tot].next=last[to]; last[to]=tot++; } bool build() { int right=0; for(int i=S;i<=T;i++) d[i]=IMAX; d[S]=0; Q[++right]=S; for(int left=1;left<=right;left++) { int now=Q[left]; for(int i=last[now];i!=-1;i=a[i].next) { int to=a[i].t; if(a[i].flow && d[now]+1<d[to]) { d[to]=d[now]+1; if(to==T) return true; Q[++right]=to; } } } return false; } int DINIC(int now,int flow) { if(!flow) return 0; if(now==T) return flow; int rem=flow,use; for(int i=last[now];i!=-1;i=a[i].next) { int to=a[i].t; if(d[to]==d[now]+1 && (use=DINIC(to,min(flow,a[i].flow)))) { a[i].flow-=use; a[i^1].flow+=use; flow-=use; } } if(rem==flow) d[now]=-1; return rem-flow; } void MaxFlow() { int use; while(build()) { while(use=DINIC(S,IMAX)) ans+=use; } } int main() { #ifndef ONLINE_JUDGE freopen("input.txt","r",stdin); freopen("output.txt","w",stdout); #endif while(scanf("%d",&N)!=EOF) { S=0;T=N*N+1;tot=0;ans=0;total=0; memset(last,-1,sizeof(last)); for(int i=1;i<=N;i++) for(int j=1;j<=N;j++) { int value; scanf("%d",&value); total+=value; if((i+j)&1) { add(S,(i-1)*N+j,value); for(int k=0;k<4;k++) if(1<=i+dx[k] && i+dx[k]<=N && 1<=j+dy[k] && j+dy[k]<=N) { int nx=i+dx[k],ny=j+dy[k]; add((i-1)*N+j,(nx-1)*N+ny,IMAX); } } else add((i-1)*N+j,T,value); } MaxFlow(); printf("%d\n",total-ans); } return 0; }