hdu 2167 Pebbles

题目大意:给定一个N*N的方格,让你在里面取出一些数使其和最大,要求每一个数不能与其相邻的8个数同时取出~~

很明显的二进制 状态压缩DP~~

先求出每一行有效的状态(即不能同时取出相邻的两个)有1597种,

我第一次就根据这直接进行了DP,DP[i][j]=max(DP[i-1][h])+sum(j);

DP[i][j]表示第i行的第j种状态,DP[i-1][h]表示第i-1行的第h中状态,sum(j)表示第i行在j状态下取出的数之和!

此时j和h要满足一定的条件。j中的每一个1不能与h中每一个1相邻,或对角相邻!

不过这样做,最后超时了。。

我想了一下,感觉每一行要进行1600*1600次循环外加一些判断 太浪费时间了,不如直接在判断出有效状态之后,直接求出那两个有效状态可以相容,

然后用邻接表进行存储,果然,效率大大提高,93MS~~~

代码:

ContractedBlock.gif ExpandedBlockStart.gif View Code
  1 # include<stdio.h>
2 # include<string.h>
3 # include<time.h>
4 int n;
5 int s[16]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768};
6 int st[1600];
7 int DP[16][1600],ans;
8 int map[16][16];
9 struct node{
10 int from,to,next;
11 }edge[3000000];
12 int head[1600],tol;
13 int max(int a,int b)
14 {
15 return a>b?a:b;
16 }
17 int judge(int i)
18 {
19 int end,cur;
20 end=-2;
21 cur=-1;
22 while(i)
23 {
24 cur++;
25 if(i%2)
26 {
27 if(cur-end==1) return 0;
28 end=cur;
29 }
30 i/=2;
31 }
32 return 1;
33 }
34 int min(int a,int b)
35 {
36 return a<b?a:b;
37 }
38 int cmp(int j,int h)
39 {
40 int i,k,flag;
41 flag=0;
42 for(i=0;i<15;i++)
43 {
44 if(j&s[i])
45 {
46 for(k=max(i-1,0);k<=min(14,i+1);k++)
47 {
48 if(h&s[k]) {flag=1;break;}
49 }
50 if(flag==1) return 0;
51 }
52 }
53 return 1;
54 }
55 void add(int a,int b)
56 {
57 edge[tol].from=a;edge[tol].to=b;edge[tol].next=head[a];head[a]=tol++;
58 }
59 int main()
60 {
61 int i,j,k,h,count,temp,i1;
62 char str[200];
63 memset(head,-1,sizeof(head));
64 tol=0;
65 k=0;
66 for(i=0;i<s[15];i++)
67 {
68 if(judge(i)) st[k++]=i;
69 }//求出有效的状态
70 for(j=0;j<k;j++)
71 {
72 for(h=j+1;h<k;h++)
73 {
74 if(cmp(st[j],st[h]))
75 {
76 add(j,h);
77 add(h,j);
78 }
79 }
80 }//判断那两个有效状态可以相邻,用邻接表存储,注意数组的大小,这个地方浪费了我好长的时间
81 while(gets(str))
82 {
83 count=0;
84 ans=0;
85 for(i=0;str[i];i++)
86 {
87 if(str[i]>='0' && str[i]<='9')
88 {
89 ans*=10;
90 ans+=str[i]-'0';
91 }
92 if(str[i]==' ') {map[0][count]=ans;count++;ans=0;}
93 }
94 map[0][count]=ans;
95 n=count+1;
96 for(i=1;i<n;i++)
97 for(j=0;j<n;j++)
98 scanf("%d",&map[i][j]);
99 getchar();
100 getchar();
101 //对输入进行控制,这个地方写的比较龊
102 memset(DP,0,sizeof(DP));
103 ans=0;
104 for(i=0;i<k;i++)
105 {
106 if(st[i]>=s[n]) break;
107 for(j=0;j<n;j++)
108 {
109 if(st[i]&s[j]) DP[0][i]+=map[0][j];
110 }
111 ans=max(ans,DP[0][i]);
112 }//先求出第0行的状态
113 for(i=1;i<n;i++)
114 {
115 for(j=0;j<k;j++)
116 {
117 if(st[j]>=s[n]) break;
118 temp=0;
119 for(h=0;h<n;h++)
120 {
121 if(st[j]&s[h]) temp+=map[i][h];
122 }
123 //求出所说的sum(j)
124 for(i1=head[j];i1!=-1;i1=edge[i1].next)
125 {
126 h=edge[i1].to;
127 if(st[h]>=s[n]) continue;
128 DP[i][j]=max(DP[i][j],DP[i-1][h]);
129 }
130 //直接同邻接表,勿须再判断
131 DP[i][j]+=temp;
132 ans=max(DP[i][j],ans);
133 }
134 }
135 printf("%d\n",ans);
136 }
137 return 0;
138 }

转载于:https://www.cnblogs.com/183zyz/archive/2011/07/31/2122748.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值