Number of Locks
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 Sample Output 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;
}