1. 程式人生 > >Tourism Planning 狀態壓縮DP(好題)

Tourism Planning 狀態壓縮DP(好題)

Tourism Planning

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 1125    Accepted Submission(s): 487

Problem Description

Several friends are planning to take tourism during the next holiday. They have selected some places to visit. They have decided which place to start their tourism and in which order to visit these places. However, anyone can leave halfway during the tourism and will never back to the tourism again if he or she is not interested in the following places. And anyone can choose not to attend the tourism if he or she is not interested in any of the places. Each place they visited will cost every person certain amount of money. And each person has a positive value for each place, representing his or her interest in this place. To make things more complicated, if two friends visited a place together, they will get a non negative bonus because they enjoyed each other’s companion. If more than two friends visited a place together, the total bonus will be the sum of each pair of friends’ bonuses. Your task is to decide which people should take the tourism and when each of them should leave so that the sum of the interest plus the sum of the bonuses minus the total costs is the largest. If you can’t find a plan that have a result larger than 0, just tell them to STAY HOME.

Input

There are several cases. Each case starts with a line containing two numbers N and M ( 1<=N<=10, 1<=M<=10). N is the number of friends and M is the number of places. The next line will contain M integers Pi (1<=i<=M) , 1<=Pi<=1000, representing how much it costs for one person to visit the ith place. Then N line follows, and each line contains M integers Vij (1<=i<=N, 1<=j<=M), 1<=Vij<=1000, representing how much the ith person is interested in the jth place. Then N line follows, and each line contains N integers Bij (1<=i<=N, 1<=j<=N), 0<=Bij<=1000, Bij=0 if i=j, Bij=Bji. A case starting with 0 0 indicates the end of input and you needn’t give an output.

Output

For each case, if you can arrange a plan lead to a positive result, output the result in one line, otherwise, output STAY HOME in one line.

Sample Input

2 1 10 15 5 0 5 5 0 3 2 30 50 24 48 40 70 35 20 0 4 1 4 0 5 1 5 0 2 2 100 100 50 50 50 50 0 20 20 0 0 0

Sample Output

5 41 STAY HOME

Source

分析:

狀壓DP有點巧妙啊

這篇題解不錯,程式碼加上了自己的註釋

題目大意:

輸入描述:

第一行兩個數字表示,有n個人,m個城市

接下來 m個數字表示每個人參觀這些城市的花費

接下來n行m列表示每個人參觀每個城市得到的滿意度

接下來n行n列表示每參觀一個城市互相之間的影響的額外滿意度,Bij (1<=i<=N, 1<=j<=N), 0<=Bij<=1000, Bij=0 if i=j, Bij=Bji.

你可以安排這n個人中的任意多個依次參觀這m個城市0~m-1,中途也可以讓一個人退出,退出後不能再回來,問你最大的值?

值 = 每個人參觀每個城市得到的滿意度的和 + 互相之間的影響增加的滿意度和 - 參觀花費和 。

解題思路:

這題的核心是DP

(1)因為n<=10 ,m<=10 ,資料比較小,可以考慮比較暴力的做法,DP就是一個很好的暴力。

(2)很容易就想到這樣的DP方程 DP[sum][k]=max{ DP[son][k+1] } + value[sum][k];

sum 就是用2進製表示的有哪些人,son就是sum的子狀態,表示sum中一些人半途離開了還剩下的人,

k表示當前是在訪問到哪個城市了,DP[sum][k]記錄的是在這個狀態下要求的最大值。

當有sum這些人訪問到k這個城市時候,這時候中途退了一些人,轉移到了son這些人,k+1城市 這個狀態 

轉移的花費就是 sum這些人在k這個城市獲得的總值,記為 value[sum][k]。

(3)唯一有點麻煩的就是value[sum][k]的資料預處理,這個暴力列舉。

程式碼:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
 
const int N=11;
int n,m;
int cost[N],a[N][N],b[N][N];
int dp[(1<<N)][N],vis[(1<<N)][N],val[(1<<N)][N],marked;
 
void input(){
	marked++;
	//輸入
	for(int i=0;i<m;i++) 
		scanf("%d",&cost[i]);
	
	for(int i=0;i<n;i++){
		for(int j=0;j<m;j++){
			scanf("%d",&a[i][j]);
		}
	}
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++){
			scanf("%d",&b[i][j]);
		}
	}
	
	//sum這些人在k這個城市獲得的總值,記為 value[sum][k],預處理
	for(int i=0;i<m;i++){
		for(int sum=0;sum<(1<<n);sum++){
			val[sum][i]=0;
			for(int p1=0;p1<n;p1++){
				//p1這個人到達了哪一個城市
				if( !((1<<p1)&sum) ) continue;
				for(int p2=0;p2<p1;p2++){
				//尋找共同狀態sum下的的人,增加相互之間的影響
					if( (1<<p2)&sum ){
						val[sum][i]+=b[p1][p2];
					}
				}
				val[sum][i]+=a[p1][i]-cost[i];
			}
		}
	}
}
 
int DP(int sum,int k){
	
	if(k>=m) return 0;
	if(vis[sum][k]==marked) return dp[sum][k];//記憶化搜尋
	int ans=0;
	for(int x=sum;x!=0;x=(x-1)&sum ){//對於每一個son,即任意缺人 ,注意二進位制運算
	/*1100100  1100000 1000100 1000000 100100 100000 100*/
		int tmp=DP(x,k+1)+val[sum][k];
		if(tmp>ans) ans=tmp;
	}
	vis[sum][k]=marked;
	return dp[sum][k]=ans;
}
 
void solve(){
	
	int ans=0;
	for(int i=0;i<(1<<n);i++){ //n個人的每一種狀態去0城市
		if(DP(i,0)>ans) ans=DP(i,0);
	}
	if(ans==0) printf("STAY HOME\n");
	else printf("%d\n",ans);
}
 
 
int main(){
	while(scanf("%d%d",&n,&m)!=EOF && (n||m) ){
		input();
		solve();
	}
	return 0;
}