HDU 4272 LianLianKan [状态压缩DP]

  之前贪心的想法确实是错误的,比赛的时候数据太水了,过了也就没有想那么多了。如果误导了他人,实在是抱歉。

  对于每个元素,最坏情况下它只能够到它后面的第9个数字,因为最坏情况下,它后面的四个数字能被它前面的四个数字消掉,这样它就能和原来是它后面的第9个元素相消了,于是我们可以用d[i][st]表示第i个数字,从i开始的10个数字的状态为st时是否可消。之后记忆化搜索即可。

  状态转移比较简单,如果st的第1位为0,说明这一位已经被消掉,d[i][st]=dp(i+1,next(st))。如果第1为为1,向后连续找至多五个为1的位,比较是否和第一位数字相同,如果相同,就将st的这两位置为0,然后d[i][st]=d(i+1,next(newst)),newst=st&~1&~(1<<k),其中x[k]==x[i]。next(st)这个函数是求将st传递到下一个位置时的状态,如果n-p<=10,则st=st>>1,否则st=1<<9|st>>1,因为只有当后面数字多于10个时,才会有新的数字加入到状态中。

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <algorithm>
 4 #define MAXN 1200
 5 using namespace std;
 6 //1未选,0已选
 7 int n, full, x[MAXN], d[MAXN][MAXN];
 8 int nextst(int p, int st) {
 9     if (n - p <= 10) return st>>1;
10     else return 1<<9|st>>1;
11 }
12 int dp(int p,int st) {
13     if (p == n) return st == 0;
14     if (d[p][st]!=-1) return d[p][st];
15     d[p][st] = 0;
16     //如果第一位为1直接去一位
17     if ((st&1) == 0) d[p][st] = dp(p+1, nextst(p, st));
18     else {
19         int cnt = 0;
20         for (int i = 1; i < 10 && cnt < 5; i++) {
21             if (1<<i&st){
22                 cnt++;
23                 if (x[i+p] != x[p]) continue;
24                 int newst = st&~1&~(1<<i);
25                 if (dp(p+1, nextst(p, newst))){
26                     d[p][st] = 1;
27                     break;
28                 }
29             }
30         }
31     }
32     return d[p][st];
33 
34 }
35 int main() {
36     while (scanf("%d",&n) !=EOF) {
37         for (int i = 1; i <= n; i++) {
38             scanf("%d", &x[n-i]);
39         }
40         memset(d, -1, sizeof d);
41         full = (1 << std::min(10, n)) - 1;
42         printf("%d\n", dp(0, full));
43     }
44 }

  

原文地址:https://www.cnblogs.com/swm8023/p/2679455.html