• 状态压缩dp(hdu2167,poj2411)


    hdu2167 http://acm.hdu.edu.cn/showproblem.php?pid=2167

    给定一个N*N的板子,里面有N*N个数字,选中一些数字,使得和最大

    要求任意两个选中的数字不相邻,相邻包括上下,左右和对角线相邻。

    由于N<=15,用程序判断了一下,每一行的有效状态<1600个,如果记录这些状态,然后每一行枚举当前行的上一行的状态那么极端下有1600*1600*15的复杂度,TLE

    所以卡在这里很久,想不到怎么优化。 然后看了别人的代码知道了用邻接表存储哪两个状态是相容的。这样就不用枚举那么多了。所以就过了。

     1 #include <stdio.h>
     2 #include <string.h>
     3 #include <iostream>
     4 #include <sstream>
     5 using namespace std;
     6 int matrix[15][15];
     7 int dp[1<<15][15],situation[1600],bit[15],head[1000000],e;
     8 struct node
     9 {
    10     int v,next;
    11 }g[1000000];;
    12 int max(const int &a, const int &b)
    13 {
    14     return a < b ? b : a;
    15 }
    16 
    17 void addEdge(int u, int v)
    18 {
    19     g[e].v = v;
    20     g[e].next = head[u];
    21     head[u] = e++;
    22 }
    23 int count(int i, int ss, int n)//计算状体第i行的状态s所取的数的和
    24 {
    25     int ret = 0;
    26     int j = 0;
    27     for(j=0; j<n; ++j)
    28     {
    29         if(bit[j] & ss)
    30             ret += matrix[i][j];
    31     }
    32     return ret;
    33 }
    34 bool check(int s, int ss,int n)//检查s和ss是否可容
    35 {
    36     
    37     if(s&ss) return false;
    38     if((s<<1)&ss) return false;
    39     if((s>>1)&ss) return false;
    40     return true;
    41 }
    42 int main()
    43 {
    44     int n,i,j,m,s,ss;
    45     char str[100];
    46     bit[i=0] = 1;
    47     for(i=1; i<15; ++i)
    48         bit[i] = bit[i-1]<<1;
    49     m = 1<<15;
    50     for(i=0,j=0; i<m; ++i)
    51         if(!(i&(i<<1)))//求出有效的状态
    52             situation[j++]= i;
    53     while(gets(str))
    54     {
    55         memset(dp,0,sizeof(dp));
    56         e = 0;
    57         memset(head,-1,sizeof(head));
    58         n = 0;
    59         do
    60         {
    61             j = 0;
    62             stringstream scin(str);
    63             while(scin>>matrix[n][j]) j++;
    64             n++;
    65             gets(str);
    66             if(str[0]=='') break;
    67 
    68         }while(true);
    69         m = 1<<n;
    70         for(i=0;situation[i]<m; ++i)//判断哪两个状体相容,然后用邻接表存储
    71             for(j=0; situation[j]<m; ++j)
    72                 if(check(situation[i],situation[j],n))
    73                     addEdge(i,j);
    74         for(i=0; situation[i]<m; ++i)
    75             dp[situation[i]][0] = count(0,situation[i],n);
    76         for(i=1; i<n; ++i)
    77             for(s=0;situation[s]<m; ++s)
    78             {
    79                 for(j=head[s];j!=-1;j=g[j].next)
    80                 {
    81                     dp[situation[g[j].v]][i] = max(dp[situation[g[j].v]][i],dp[situation[s]][i-1]+count(i,situation[g[j].v],n));
    82                 }
    83             }
    84         int ans = 0;
    85         for(i=0; situation[i]<m; ++i)
    86         {
    87             ans = max(ans,dp[situation[i]][n-1]);
    88         }
    89         printf("%d
    ",ans);
    90     }
    91     return 0;
    92 }

     poj2411 http://poj.org/problem?id=2411

    给定一个N*M的矩形,用1*2的矩阵填充满,问有多少种填充的方法。 N,M<=11

    状态压缩dp,时间复杂度是N*(1<<M)*(1<<M),可以使用邻接表存储,哪些状态是相容的,这样子,就不用枚举所有的状态了。

    状态是如何转移的呢?

    首先第一行的状态必须是:如果有1,那么必须有连续的两个1.即不能有单独的一个1.

    这样子是为了,第二行如果竖着放,那么就可以填充第一行的空缺。

    怎么判断当前行的状态和上一行的状态不冲突的?详见bool check(int s, int ss)函数注释

     1 #include <stdio.h>
     2 #include <string.h>
     3 #define LL __int64
     4 int n,m;
     5 int e,head[1000000];
     6 LL dp[1<<11][11];
     7 struct node
     8 {
     9     int v,next;
    10 }g[1000000];
    11 
    12 void addEdge(int a, int b)
    13 {
    14     g[e].v = b;
    15     g[e].next = head[a];
    16     head[a] = e++;
    17 }
    18 void swap(int &a, int &b)
    19 {
    20     int t = a;
    21     a = b;
    22     b = t;
    23 }
    24 bool check(int s)
    25 {
    26     while(s)
    27     {
    28         if( (s&1) && ((s>>1)&1))
    29             s>>=2;
    30         else if( (s&1) && !((s>>1)&1))
    31             return false;
    32         else if(!(s&1))
    33             s>>=1;
    34         
    35     }
    36     return true;
    37 }
    38 bool check(int s, int ss)
    39 {
    40     for(int i=0; i<m; ++i)
    41     {
    42         if(!(s&1) && !(ss&1)) return false;//上一行该位置为0,那么这一行该位置应该竖着放,否则不相容
    43         else if(!(s&1)&&(ss&1)) 
    44         {
    45             s>>=1;ss>>=1;continue;//上一行为0,这一行竖着放
    46         }
    47         else if((s&1)&&!(ss&1)) 
    48         {
    49             s>>=1;ss>>=1;continue;//上一行该位置为1,这一行可以不放
    50         }
    51         else if((s&1)&&(ss&1))//上一行的该位置和这一行的该位置为1,那么这行的矩形是横着放
    52         {
    53             s>>=1,ss>>=1;i++;
    54             if((s&1)&&(ss&1)) //即下一个位置,上一行和这一行都必须为1
    55             {
    56                 s>>=1;ss>>=1;continue;
    57             }
    58             else return false;
    59         }
    60     }
    61     return true;
    62 }
    63 int main()
    64 {    
    65     int s,ss,t,i,j;
    66     LL ans;
    67     while(scanf("%d%d",&n,&m),n)
    68     {
    69         memset(dp,0,sizeof(dp));
    70         if(n<m) swap(n,m);
    71         ans = 0;
    72         t = 1<<m;
    73         memset(head,-1,sizeof(head));
    74         e = 0;
    75         for(s=0; s<t; ++s)
    76             for(ss=0; ss<t; ++ss)
    77                 if(check(s,ss))
    78                     addEdge(s,ss);
    79         if((n*m)%2==0)
    80         {
    81             for(s=0; s<t; ++s)
    82                 if(check(s))
    83                     dp[s][0] = 1;
    84             for(i=1; i<n; ++i)
    85                 for(s=0; s<t; ++s)
    86                     for(j=head[s];j!=-1 && g[j].v<t;j=g[j].next)
    87                         dp[g[j].v][i] += dp[s][i-1];
    88                         
    89             ans = dp[(1<<m)-1][n-1];
    90         }
    91         printf("%I64d
    ",ans);
    92     }
    93     return 0;
    94 }
  • 相关阅读:
    快速幂取模
    程序人生系列之新闻发布系统 0105
    JavaWeb之博客系统(四)
    [转]树状数组
    题目:免费午餐
    题目:删数问题
    题目:三元组
    题目:分子团
    题目:[汪老师结婚]婚礼上的袭击
    题目:[SBN号码]
  • 原文地址:https://www.cnblogs.com/justPassBy/p/4314297.html
Copyright © 2020-2023  润新知