天天看点

POJ-1351 Number of Locks

Number of Locks
Time Limit: 1000MS Memory Limit: 10000K

Description

In certain factory a kind of spring locks is manufactured. There are n slots (1 < n < 17, n is a natural number.) for each lock. The height of each slot may be any one of the 4 values in{1,2,3,4}( neglect unit ). Among the slots of a lock there are at least one pair of neighboring slots with their difference of height equal to 3 and also there are at least 3 different height values of the slots for a lock. If a batch of locks is manufactured by taking all over the 4 values for slot height and meet the two limitations above, find the number of the locks produced.

Input

There is one given data n (number of slots) on every line. At the end of all the input data is -1, which means the end of input.

Output

According to the input data, count the number of locks. Each output occupies one line. Its fore part is a repetition of the input data and then followed by a colon and a space. The last part of it is the number of the locks counted.

Sample Input

2
3
-1
      
Sample Output
2: 0
3: 8
      

Source

Xi'an 2002

————————————————————罪恶感的分割线————————————————————

思路:据Roll神说,DP要靠题量的积累。唉真是忧桑。没有题量积累的我只能依赖题解了……

递推公式我并不擅长,我们来学一下dfs+记忆化的姿势吧!给数学跪烂。

首先刻画状态,这是DP题的第一步。稍等,为什么这道题就要用DP的思想来做呢?因为……对于给定的槽数,从第一个槽开始,它有四个状态:第几个槽;高度;之前是否出现过高度差为3;使用了几种高度。这四个状态可以依次向后转移。

状态已经出来了,四维DP。dp[i][high][k][diff]。我们要的状态是:diff >= 3且k = 1。但是对于使用了几种高度(diff),我们要怎么转移呢?答案是——状态压缩。用s表示用了哪几种颜色,位运算一下即可。

那么dfs(i, high, k, diff, s)也出来了。初始化为-1就可以进行记忆化。递归的边界是最后一个槽,符合题意返回1,否则返回0。

然后是For(四种高度),转移状态,利用ans统计可行状态数。最后return dp[][][][] = ans;

代码如下:

#include <cstdio>
 #include <cstdlib>
 #include <cstring>
 #include <algorithm>
 #include <cmath>
 #include <stack>
 #include <queue>
 #include <vector>
 #include <map>
 #include <string>
 #include <iostream>
 using namespace std;

#define New (1<<(d-1))
const int N = 20;
int n;
long long dp[N][5][2][4];
//i————第i个槽;high————当前槽高度;k————是否存在高度差为3的槽;diff————使用了几种高度;s————四种高度的状态压缩
long long dfs(int i, int high, int k, int diff, int s)
{
	if(dp[i][high][k][diff] != -1)
		return dp[i][high][k][diff];//记忆化
	if(i == n) {//递归边界:第n个槽
		if(k && diff >= 3)//有高度差为3且使用了3种以上的槽
			return 1;
		else	
			return 0;
	}
	long long ans = 0;
	int tmp;
	for(int d = 1; d <= 4; d++) {//dfs四种高度
		if(!(s & New))//没用过这种高度
			tmp = diff + 1;
		else 
			tmp = diff;
		tmp = min(tmp, 3);//剪枝,用过四种或三种高度都是“三种以上”
		if(k || ((d * high == 4) && d != high))
			ans += dfs(i+1, d, 1, tmp, s|New);
		else
			ans += dfs(i+1, d, 0, tmp, s|New);
	}
	return dp[i][high][k][diff] = ans;
}

int main()
{
	while(~scanf("%d", &n)) {
		if(n == -1)	break;
		printf("%d: ", n);
		if(n < 3)
			puts("0");
		else {
			memset(dp, -1, sizeof(dp));
			dfs(0, 0, 0, 0, 0);//全为0是初态,递归之后全为0就是末态
			printf("%lld\n", dp[0][0][0][0]);
		}
	}
	return 0;
}