状态DP

1.poj 1185

  题意:炮兵阵地有n行m列,n<=100,m<=10,故可以使用将每行放置大炮的情况状态压缩为二进制的形式。
    状态数目最多约为60种,dp[r][i][k]表示在r行状态为k,r-1行状态为i,前r行最多可以摆放的大炮数目。
    之后枚举r行
    状态转移:dp[r][j][i] = max{dp[r][j][i],dp[r-1][k][j]+sum[i]}
    最为关键的是将题目模型化嵌套入状态压缩的模型,而且要熟悉各种位运算的用法。

View Code
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 
 6 using namespace std;
 7 
 8 int cnt,map[105],dp[105][65][65],sum[65],stk[65];
 9 
10 int  ok(int x)
11 {
12     if(x&(x<<1)||x&(x<<2))
13         return 0;
14     return 1;
15 }
16 
17 int add(int x)
18 {
19     return x==0?0:add(x-(x&-x))+1;
20 }
21 
22 void init(int row,int col)
23 {
24     int r,c;
25     char temp;
26     memset(map,0,sizeof(map));
27     for(r=0;r<row;r++)
28     {
29         getchar();
30         for(c=0;c<col;c++)
31         {
32             scanf("%c",&temp);
33             if(temp=='H')
34                 map[r]|=(1<<c);
35         }
36     }
37 }
38 
39 void get(int n)
40 {
41     int i;
42     cnt=0;
43     for(i=0;i<(1<<n);i++)
44     {
45         if(ok(i))
46         {
47             stk[cnt]=i;
48             sum[cnt++]=add(i);
49         }
50     }
51 }
52 
53 int main()
54 {
55     int row,col,i,j,r,c,k,ans;
56     while(scanf("%d%d",&row,&col)!=EOF)
57     {
58         ans=0;
59         memset(dp,-1,sizeof(dp));
60         init(row,col);
61         get(col);
62         for(i=0;i<cnt;i++)
63             if(!(stk[i]&map[0]))
64                 dp[0][0][i]=sum[i];
65         for(r=1;r<row;r++)
66         {
67             for(i=0;i<cnt;i++)
68             {
69                 if(stk[i]&map[r])
70                     continue;
71                 for(j=0;j<cnt;j++)
72                 {
73                     if(stk[j]&stk[i])
74                         continue;
75                     for(k=0;k<cnt;k++)
76                     {
77                         if(stk[k]&stk[i])
78                             continue;
79                         if(dp[r-1][k][j]==-1)
80                             continue;
81                         dp[r][j][i]=max(dp[r][j][i],dp[r-1][k][j]+sum[i]);
82                     }
83                 }
84             }
85         }
86         for(i=0;i<cnt;i++)
87             for(j=0;j<cnt;j++)
88             {
89                 if(ans<dp[row-1][i][j])
90                     ans=dp[row-1][i][j];
91             }
92         printf("%d\n",ans);
93     }
94     return 0;
95 }

 

2.poj 3254

 题意:其实就是炮兵阵地的简单版,实质上算是枚举了一遍所有状态的组合,从而求得总的方案数。
    在学习时要多思考,敲码实现与做的题的多少都没有关系,最重要的是通过做题去深刻理解一个算法的思想,并学会运用这些思想。

View Code
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 
 6 using namespace std;
 7 const int MAX=400,MOD=100000000;
 8 
 9 int cnt,stk[MAX],map[MAX],dp[15][MAX];
10 int ok(int n)
11 {
12     if(n&n<<1)
13         return 0;
14     return 1;
15 }
16 
17 void get(int n)
18 {
19     int i;
20     cnt=0;
21     for(i=0;i<(1<<n);i++)
22     {
23         if(ok(i))
24             stk[cnt++]=i;
25     }
26 }
27 
28 int main()
29 {
30     int row,col,r,c,x,i,j,sum;
31     while(scanf("%d%d",&row,&col)!=EOF)
32     {
33         sum=0;
34         memset(map,0,sizeof(map));
35         memset(dp,0,sizeof(dp));
36         for(r=0;r<row;r++)
37             for(c=0;c<col;c++)
38             {
39                 scanf("%d",&x);
40                 if(x==0)
41                     map[r]|=(1<<c);
42             }
43         get(col);
44         for(i=0;i<cnt;i++)
45             if(!(map[0]&stk[i]))
46                 dp[0][i]=1;
47         for(r=1;r<row;r++)
48         {
49             for(i=0;i<cnt;i++)
50             {
51                 if(map[r]&stk[i])
52                     continue;
53                 for(j=0;j<cnt;j++)
54                 {
55                     if(map[r-1]&stk[j]||stk[i]&stk[j])
56                         continue;
57                     dp[r][i]+=dp[r-1][j];
58                     dp[r][i]%=MOD;
59                 }
60             }
61         }
62         for(i=0;i<cnt;i++)
63         {
64             sum+=dp[row-1][i];
65             sum%=MOD;
66         }
67         printf("%d\n",sum);
68     }
69     return 0;
70 }

3.poj 2411

  题意:给出一个长为n,宽为m的矩形,要求得出用多个长为2宽为1的小矩形组成该矩形的方案总数。
    由于0<n,m<=11, 且求最值问题,故联想到状态DP,此题的难点在于状态的设计,在这里我将横着放置的小矩形用(1 1)横向表示,
    竖着放置的小矩形用(0,1),(1,0)纵向表示。判断合法则要根据小矩形放置情况去判定,若在i位置为1,则应判定i+1位置也为1,i+=2;
    若为0,则i++;同时要判定i+1>=len;具体看ok()函数。
    在第一行时需要得出所有符合条件的状态,之后就需要判断两行之间矩形的组合情况是否合法。这里依然借鉴之前的判断方法,
    若两行的i位置均出现1,则两行都存在横着的小矩形,若两行的i位置分别出现0或1,则代表两行之间出现竖着放置的小矩形,若两行
    的i位置均出现0,则不符合情况。
    
    最重要的就是巧妙的设计状态表示,以及状态合法情况的判断。

View Code
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 
 6 using namespace std;
 7 typedef long long ll;
 8 int row,col;
 9 ll dp[12][3000];
10 
11 int ok(int n)
12 {
13     int i,j,len;
14     len=col;
15     for(i=0;i<len;)
16     {
17         j=n&(1<<i);
18         if(j>0)
19         {
20             if(i+1>=len)
21                 return 0;
22             if((n&(1<<(i+1)))==0)
23                return 0;
24             i+=2;
25         }
26         else
27             i++;
28     }
29     return 1;
30 }
31 
32 int okk(int n,int m)
33 {
34     int i,x,y,len;
35     len=col;
36     for(i=0;i<len;)
37     {
38         x=n&(1<<i);
39         y=m&(1<<i);
40         if(x==0&&y==0)
41             return 0;
42         if(x>0&&y>0)
43         {
44             if(i+1>=len)
45                 return 0;
46             if((n&(1<<(i+1)))==0||(m&(1<<(i+1)))==0)
47                 return 0;
48             i+=2;
49         }
50         else
51             i++;
52     }
53     return 1;
54 }
55 
56 int main()
57 {
58     int s,r,c,i,j;
59     while(scanf("%d%d",&row,&col)!=EOF)
60     {
61         if(row==0&&col==0)
62             break;
63         if(col>row)
64             swap(col,row);
65         memset(dp,0,sizeof(dp));
66         s=1<<col;
67         for(i=0;i<s;i++)
68          {
69             if(ok(i))
70                dp[0][i]=1;
71          }
72         for(r=1;r<row;r++)
73         {
74             for(i=0;i<s;i++)
75             {
76                 for(j=0;j<s;j++)
77                 {
78                     if(okk(i,j))
79                         dp[r][i]+=dp[r-1][j];
80                 }
81             }
82         }
83       printf("%lld\n",dp[row-1][s-1]);
84     }
85     return 0;
86 }

 

待续...

 

posted @ 2012-08-06 15:57  hankers  阅读(269)  评论(0编辑  收藏  举报