HDU 4084 Campus Design 状压dp

来源:互联网 发布:db2数据库的一些操作 编辑:程序博客网 时间:2024/05/29 19:48

题意:在n*m的格子里面放1*1和1*2的砖块,要求不能放在格子为0的地方,并且使用1*1的数目在[c,d]之间,求方案总数。

想了一天也没有想到有效的转移,其实是陷入了一种错误的想法中,转移应该是一块一块的,不应该是一层一层的,终于在看过了别人的代码以后明白了。

又跟学长交流了一下,经过学长的指导,发现是自己想轴了,一层一层的转移也是可行的,比较一下两个方式的复杂度,一块一块的转移是O(n*m*d*2^m)1437 ms,一层一层的转移是O(n*d*2^m*dfs一层的复杂度)6406 ms,试了一下,也是可以AC的。

一块一块的转移

#include <iostream>  #include <stdio.h>  #include <string.h>    using namespace std;  const int mod=1000000007;  char s[110][12];  int n,m,c,d;  int dp[2][1100][25];  int now,next;  void update(int nextp,int p,int nextk,int k)  {      dp[next][nextp][nextk]+=dp[now][p][k];      if (dp[next][nextp][nextk]>=mod)           dp[next][nextp][nextk]-=mod;  }  int main()  {     // freopen("in.txt","r",stdin);      while (~scanf("%d%d%d%d",&n,&m,&c,&d))      {          for (int i=1;i<=n;i++)              scanf("%s",s[i]);          memset(dp,0,sizeof(dp));          dp[0][(1<<m)-1][0]=1;            now=0;            for (int i=1;i<=n;i++)              for (int j=0;j<m;j++)          {              next=now^1;              //状态表示的是当前格子的前m块格子是否放砖的情况;              if (s[i][j]=='0')              {                  for (int k=0;k<=d;k++)                      for (int p=(1<<(m-1));p<(1<<m);p++)//格子是'0',什么也不放,并且必须保证这个格子的上面那个格子放过了;                      if (dp[now][p][k])                      update(((p<<1)-(1<<m))^1,p,k,k);              }              else//格子是'1';              {                  for (int k=0;k<=d;k++)                  for (int p=(1<<(m-1));p<(1<<m);p++)//什么也不放,并且必须保证这个格子的上面那个格子放过了;                  if (dp[now][p][k])                  update(((p<<1)-(1<<m)),p,k,k);                    for (int k=0;k<=d;k++)                  for (int p=(1<<(m-1));p<(1<<m);p++)//放1*1,并且必须保证这个格子的上面那个格子放过了;                  if (dp[now][p][k])                  update(((p<<1)-(1<<m))^1,p,k+1,k);                    if (j!=0)                  {                      for (int k=0;k<=d;k++)                      for (int p=(1<<(m-1));p<(1<<m);p++)//横放1*2,并且必须保证这个格子的上面那个格子放过了,前面的格子是空的;                      if (!(p&1)&&dp[now][p][k]) update(((p<<1)-(1<<m))^3,p,k,k);                  }                    for (int k=0;k<=d;k++)                  for (int p=0;p<(1<<(m-1));p++)//竖放1*2,并且必须保证这个格子的上面的格子是空的;                  if (dp[now][p][k])                  update((p<<1)^1,p,k,k);              }              memset(dp[now],0,sizeof(dp[now]));              now^=1;          }          int ans=0;            for (int k=c;k<=d;k++)          {              ans+=dp[now][(1<<m)-1][k];              if (ans>=mod) ans-=mod;          }        }        return 0;  } 



一层一层的转移:


#include <iostream>  #include <stdio.h>  #include <string.h>    using namespace std;  const int mod=1000000007;  char s[110][12];  int n,m,c,d;  int dp[110][1100][25];  void dfs(int i,int j,int k,int num,int p,int kk)  {      if (num>=m)//达到一种状态;      {          if(k+kk<=d)          {              dp[i+1][p][k+kk]+=dp[i][j][k];              if (dp[i+1][p][k+kk]>=mod) dp[i+1][p][k+kk]-=mod;          }          return;      }      if (j&(1<<num))//当前位置已放;      {          if (s[i+1][num]=='0')          dfs(i,j,k,num+1,p+(1<<num),kk);          else dfs(i,j,k,num+1,p,kk);          return;      }      if (s[i+1][num]=='0') //放1*1          dfs(i,j,k,num+1,p+(1<<num),kk+1);          else dfs(i,j,k,num+1,p,kk+1);        if (num<m-1&&!(j&(1<<(num+1))))//横放1*2;      {          int pp=p;          if (s[i+1][num]=='0') pp+=(1<<num);          if (s[i+1][num+1]=='0') pp+=(1<<(num+1));          dfs(i,j,k,num+2,pp,kk);      }      if (s[i+1][num]=='1')//竖放1*2;         dfs(i,j,k,num+1,p+(1<<num),kk);        return;  }  int main()  {     // freopen("in.txt","r",stdin);      while (~scanf("%d%d%d%d",&n,&m,&c,&d))      {          for (int i=1;i<=n;i++)              scanf("%s",s[i]);                for (int i=0;i<m;i++)                  s[n+1][i]='0';            memset(dp,0,sizeof(dp));          int sum=(1<<m)-1;            int p=0;          for (int i=0;i<m;i++)//把最后一行的下面一行填满;              if (s[1][i]=='0') p+=(1<<i);            dp[1][p][0]=1;            for (int i=1;i<=n;i++)          {              for (int j=0;j<=sum;j++)              for (int k=0;k<=d;k++)              if (dp[i][j][k])              {                  dfs(i,j,k,0,0,0);              }          }            int ans=0;          for(int i=c;i<=d;i++)          {              ans+=dp[n+1][sum][i];              if (ans>=mod) ans-=mod;            }          printf("%d\n",ans);      }      return 0;      }




原创粉丝点击