1. 程式人生 > >HDU 1565 方格取數 題解

HDU 1565 方格取數 題解

【題目】:

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;
}