zoukankan      html  css  js  c++  java
  • hdu 1565 方格取数(1) 状态压缩dp

    方格取数(1)

    Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
    Total Submission(s): 3848    Accepted Submission(s): 1473


    Problem Description
    给你一个n*n的格子的棋盘,每个格子里面有一个非负数。
    从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数的和最大。
     
    Input
    包括多个测试实例,每个测试实例包括一个整数n 和n*n个非负数(n<=20)
     
    Output
    对于每个测试实例,输出可能取得的最大的和
     
    Sample Input
    3
    75 15 21
    75 15 28
    34 70 5
     
    Sample Output
    188
     
    Author
    ailyanlu
     超内存,用滚动数组了,超时!
    算一下时间复杂度 20*20*2^20*2^20 额额。。。。
     
    超时代码留个标记
     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstdlib>
     4 #include<cstring>
     5 using namespace std;
     6 
     7 int a[21][21];
     8 int dp1[1<<21],before[1<<21],now[1<<21];
     9 
    10 void prepare(int i,int n)//预处理
    11 {
    12     int j,k,s;
    13     k=1<<n;
    14     for(j=0; j<k; j++)
    15     {
    16         dp1[j]=0;
    17         for(s=1; s<=n; s++)
    18         {
    19             if( (j&(1<<(s-1))) == (1<<(s-1)) )//包含了这个数字
    20                 dp1[j]=dp1[j]+a[i][s];
    21         }
    22     }
    23 }
    24 bool panduan(int b,int n)
    25 {
    26     int i,x;
    27     x=3;
    28     for(i=1; i<n; i++)
    29     {
    30         if( (x&b) == x) return false;
    31         x=x<<1;
    32     }
    33     return true;
    34 }
    35 int main()
    36 {
    37     int n;
    38     int i,j,s,k,hxl;
    39     scanf("%d",&n);
    40     {
    41         for(i=1; i<=n; i++)
    42             for(j=1; j<=n; j++)
    43                 scanf("%d",&a[i][j]);
    44         memset(now,0,sizeof(now));
    45         memset(before,0,sizeof(before));
    46         k=1<<n;
    47         for(i=1; i<=n; i++)
    48         {
    49             prepare(i,n);
    50             for(j=0; j<k; j++)
    51                 if(panduan(j,n))
    52                 {
    53                     hxl=0;
    54                     for(s=0; s<k; s++)
    55                     {
    56                         if( (s&j)>0 ) continue;
    57                         if(before[s]>hxl)
    58                             hxl=before[s];
    59                     }
    60                     dp1[j]=dp1[j]+hxl;
    61                 }
    62             for(j=0; j<k; j++)
    63             {
    64                 now[j]=dp1[j];
    65                 before[j]=now[j];
    66             }
    67         }
    68         hxl=0;
    69         for(i=0; i<k; i++)
    70             if(now[i]>hxl) hxl=now[i];
    71 
    72         printf("%d
    ",hxl);
    73     }
    74     return 0;
    75 }

     优化:

      很多地方可以优化。

      1.其实,可以预处理相邻的情况。不要每一次都判断。就像打表一样。//怎么处理的呢?具体看一下代码。

      2.预处理相邻的情况后,发现,1<<20的数组,满足条件的只有17711种,这样的话,优化就巨大了。

        3.滚动数组。

          我的代码,还是不够优化的,960ms

         

     1 #include<stdio.h>
     2 #include<stdlib.h>
     3 #include<string.h>
     4 
     5 int a[22][22];
     6 int state[17715],len;
     7 int dp[1<<22],befor[1<<22];
     8 
     9 void make_init()//预处理,所以的状态
    10 {
    11     int i,k;
    12     k=1<<20;
    13     len=0;
    14     for(i=0;i<k;i++)
    15     {
    16         if( (i&(i<<1))>0 ) continue;
    17         else state[len++]=i;
    18     }
    19 }
    20 void prepare(int r,int n)//处理每一行的dp值.
    21 {
    22     int i,j,k,ans,x;
    23     k=1<<n;
    24 
    25     for(j=0;state[j]<k;j++)
    26     {
    27         i=state[j];
    28         dp[i]=0;
    29         x=i;
    30         ans=1;
    31         while(x)
    32         {
    33             if(x&1) dp[i]=dp[i]+a[r][ans];
    34             ans++;
    35             x=x>>1;
    36         }
    37     }
    38 }
    39 
    40 int main()
    41 {
    42     int n;
    43     int i,j,k,s,hxl;
    44     make_init();
    45     while(scanf("%d",&n)>0)
    46     {
    47         for(i=1;i<=n;i++)
    48         for(j=1;j<=n;j++)
    49         scanf("%d",&a[i][j]);
    50 
    51         k=1<<n;
    52         memset(befor,0,sizeof(befor));
    53         for(i=1;i<=n;i++) //枚举每一行
    54         {
    55             prepare(i,n);
    56             for(j=0;state[j]<k;j++)//枚举每一种 有效 状态
    57             {
    58                 hxl=0;
    59                 for(s=0;state[s]<k;s++)//上一行的有效状态
    60                 {
    61                     if( (state[j]&state[s]) >0) continue;//没有冲突
    62                     if(befor[state[s]]>hxl)
    63                     hxl=befor[state[s]];
    64                 }
    65                 dp[state[j]]=dp[state[j]]+hxl;
    66             }
    67             for(j=0;state[j]<k;j++)
    68             {
    69                 befor[state[j]]=dp[state[j]];
    70             }
    71         }
    72         hxl=0;
    73         for(i=0;state[i]<k;i++)
    74         if(hxl<befor[state[i]]) hxl=befor[state[i]];
    75         printf("%d
    ",hxl);
    76     }
    77     return 0;
    78 }

    进一步的优化:

      看了一下,别人的博客,看到一个很别人开的数组是很小的。why?

          我开的dp数组是 dp[1<<22],其实只要开dp[17715];和状态state[17715 ]一样就可以了.

    为什么可以呢?

    简单说明一下:原来的方法,是和最原始的位置是一一对应,所以,很好理解。

    但是,有很多的空间是浪费的。

    现在是对应状态一一对应,就是这样。这样数组开的就小很多了。而且速度快一些。

    921ms  -->   486 ms

     1 #include<stdio.h>
     2 #include<stdlib.h>
     3 #include<string.h>
     4 
     5 int a[22][22];
     6 int state[17715],len;
     7 int dp[17715],befor[17715];
     8 
     9 void make_init()//预处理,所以的状态
    10 {
    11     int i,k;
    12     k=1<<20;
    13     len=0;
    14     for(i=0;i<k;i++)
    15     {
    16         if( (i&(i<<1))>0 ) continue;
    17         else state[len++]=i;
    18     }
    19 }
    20 void prepare(int r,int n)//处理每一行的dp值.
    21 {
    22     int j,k,ans,x;
    23     k=1<<n;
    24 
    25     memset(dp,0,sizeof(dp));
    26     for(j=0;state[j]<k;j++)
    27     {
    28         x=state[j];
    29         ans=1;
    30         while(x)
    31         {
    32             if(x&1) dp[j]=dp[j]+a[r][ans];//!!!哈哈。
    33             ans++;
    34             x=x>>1;
    35         }
    36     }
    37 }
    38 
    39 int main()
    40 {
    41     int n;
    42     int i,j,k,s,hxl;
    43     make_init();
    44     while(scanf("%d",&n)>0)
    45     {
    46         for(i=1;i<=n;i++)
    47         for(j=1;j<=n;j++)
    48         scanf("%d",&a[i][j]);
    49 
    50         k=1<<n;
    51         memset(befor,0,sizeof(befor));
    52         for(i=1;i<=n;i++) //枚举每一行
    53         {
    54             prepare(i,n);
    55             for(j=0;state[j]<k;j++)//枚举每一种 有效 状态
    56             {
    57                 hxl=0;
    58                 for(s=0;state[s]<k;s++)//上一行的有效状态
    59                 {
    60                     if( (state[j]&state[s]) >0) continue;//没有冲突
    61                     if(befor[s]>hxl)
    62                     hxl=befor[s];
    63                 }
    64                 dp[j]=dp[j]+hxl;
    65             }
    66             for(j=0;state[j]<k;j++)
    67             {
    68                 befor[j]=dp[j];
    69             }
    70         }
    71         hxl=0;
    72         for(i=0;state[i]<k;i++)
    73         if(hxl<befor[i]) hxl=befor[i];
    74         printf("%d
    ",hxl);
    75     }
    76     return 0;
    77 }
  • 相关阅读:
    html5-特殊符号的使用
    html5-表格
    html5-列表
    html5-绝对路径/相对路径
    html5-嵌入图片
    html5-超级链接
    html5-常用的文本元素
    html5-了解元素的属性
    Scanner类throwFor(Unknown Source)及跳过下一个扫描器分析
    有关HashMap的一些问题及解答
  • 原文地址:https://www.cnblogs.com/tom987690183/p/3420156.html
Copyright © 2011-2022 走看看