1. 程式人生 > >【bzoj1187】[HNOI2007]神奇遊樂園 插頭dp

【bzoj1187】[HNOI2007]神奇遊樂園 插頭dp

http oid sam zoj str 遊樂場 tdi eof 輸入

題目描述

給你一個 m * n 的矩陣,每個矩陣內有個權值V(i,j) (可能為負數),要求找一條回路,使得每個點最多經過一次,並且經過的點權值之和最大。

輸入

輸入文件中的第一行為兩個正整數n和m,表示遊樂場的大小為n×m。因為這個娛樂場很狹窄,所以n和m滿足:2<=n<=100,2<=m<=6。接下來的n行,每行有m個整數,第i行第j列表示遊樂場的第i行第j列的小格子中的娛樂項目的滿意度,這個滿意度的範圍是[-1000,1000]。同一行的兩個整數之間用空格隔開。

輸出

輸出文件中僅一行為一個整數,表示最高的滿意度之和。

樣例輸入

4 4
100 300 -400 400

-100 1000 1000 1000
-100 -100 -100 -100
-100 -100 -100 1000

樣例輸出

4000


題解

插頭dp

設 $f[i][j][k]$ 表示輪廓線為第 $i$ 行第 $j$ 列,插頭狀態為 $k$ 的最大滿意度。那麽就和 哈密頓回路計數 那題按照同樣的方法轉移即可。

註意本題不要求走遍所有格子,因此更新答案的時機應該為:左插頭為左括號,上插頭為右括號,其余位置沒有插頭。此時更新答案。註意此時不要轉移,因為只能選擇一條回路,不能選擇多條。

時間復雜度 $O(能過)$

#include <cstdio>
#include <cstring>
int m , f[110][7][130] , a[110][7] , b[7] , w[2200] , v[130] , tot;
inline void gmax(int &a , int b)
{
	a = (a > b ? a : b);
}
void dfs(int p , int c , int now)
{
	if(c < 0 || c > m - p + 1) return;
	if(p > m)
	{
		w[now] = ++tot , v[tot] = now;
		return;
	}
	dfs(p + 1 , c , now);
	dfs(p + 1 , c + 1 , now + b[p]);
	dfs(p + 1 , c - 1 , now + 2 * b[p]);
}
inline int right(int v , int p)
{
	int i , c = 0;
	for(i = p ; i <= m ; i ++ )
	{
		if(v / b[i] % 3 == 1) c ++ ;
		if(v / b[i] % 3 == 2) c -- ;
		if(!c) return i;
	}
	return -1;
}
inline int left(int v , int p)
{
	int i , c = 0;
	for(i = p ; ~i ; i -- )
	{
		if(v / b[i] % 3 == 2) c ++ ;
		if(v / b[i] % 3 == 1) c -- ;
		if(!c) return i;
	}
	return -1;
}
int main()
{
	int n , i , j , k , p , q , ans = -1 << 30;
	scanf("%d%d" , &n , &m);
	for(i = 1 ; i <= n ; i ++ )
		for(j = 1 ; j <= m ; j ++ )
			scanf("%d" , &a[i][j]);
	b[0] = 1;
	for(i = 1 ; i <= m ; i ++ ) b[i] = b[i - 1] * 3;
	dfs(0 , 0 , 0);
	memset(f , 0xc0 , sizeof(f));
	f[1][0][w[0]] = 0;
	for(i = 1 ; i <= n ; i ++ )
	{
		for(j = 1 ; j <= m ; j ++ )
		{
			for(k = 1 ; k <= tot ; k ++ )
			{
				p = v[k] / b[j - 1] % 3 , q = v[k] / b[j] % 3;
				if(!p && !q) gmax(f[i][j][k] , f[i][j - 1][k]);
				if(!p && !q && i < n && j < m) gmax(f[i][j][w[v[k] + b[j - 1] + 2 * b[j]]] , f[i][j - 1][k] + a[i][j]);
				if(!p && q)
				{
					if(j < m) gmax(f[i][j][k] , f[i][j - 1][k] + a[i][j]);
					if(i < n) gmax(f[i][j][w[v[k] + q * (b[j - 1] - b[j])]] , f[i][j - 1][k] + a[i][j]);
				}
				if(p && !q)
				{
					if(i < n) gmax(f[i][j][k] , f[i][j - 1][k] + a[i][j]);
					if(j < m) gmax(f[i][j][w[v[k] + p * (b[j] - b[j - 1])]] , f[i][j - 1][k] + a[i][j]);
				}
				if(p == 1 && q == 1) gmax(f[i][j][w[v[k] - b[j - 1] - b[j] - b[right(v[k] , j)]]] , f[i][j - 1][k] + a[i][j]);
				if(p == 2 && q == 2) gmax(f[i][j][w[v[k] - 2 * b[j - 1] - 2 * b[j] + b[left(v[k] , j - 1)]]] , f[i][j - 1][k] + a[i][j]);
				if(p == 2 && q == 1) gmax(f[i][j][w[v[k] - 2 * b[j - 1] - b[j]]] , f[i][j - 1][k] + a[i][j]);
				if(p == 1 && q == 2 && !(v[k] - b[j - 1] - 2 * b[j])) gmax(ans , f[i][j - 1][k] + a[i][j]);
			}
		}
		for(j = 1 ; j <= tot ; j ++ )
			if(v[j] % 3 == 0)
				f[i + 1][0][j] = f[i][m][w[v[j] / 3]];
	}
	printf("%d\n" , ans);
	return 0;
}

【bzoj1187】[HNOI2007]神奇遊樂園 插頭dp