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\n",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\n",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\n",hxl);
75     }
76     return 0;
77 }

 

 

 

 

 

posted @ 2013-11-12 20:09  芷水  阅读(172)  评论(0编辑  收藏  举报