hihoCoder第八周——状态压缩·一
题目链接:http://hihocoder.com/problemset/problem/1044
注意点
动态规划,状态压缩
1、best[i][j]表示在j决策的情况下,计算到第i个座位时可以清理的最多垃圾数。
2、解释下j决策:
因为考虑到计算i+1行的数据时(即清扫到i+1个座位时各种决策下的最大清理垃圾数),需要考虑如果清扫该座位的垃圾会不会造成纠纷(即连续的M个座位中有Q个以上的座位被清扫)。所以我们应该把清扫到第i个座位并取得最大清扫数时,该座位前的M个座位(包括第i个座位)是否清扫记录下来。用01串记录,比如:M=3,i=4,如果在清扫座位2,4时获得最大清扫数,此时决策j用01串表示就是101(从左到右依次表示:座位2清扫,座位3不清扫,座位4清扫),把101转化为5,所以得到best[4][5]。
3、如何计算best[i][j]
1)首先如果one_num(j)(决策j包含的1的个数)大于Q,best[i][j]=0
2)如果one_num(j)小于Q,那么座位 i 的决策 j 可能由座位 i-1 的决策 j/2 或者 j/2 + 1<<(M-1) 得来。而且j的最后一位为1,代表清扫第i个座位,j的最后一位为0,代表没清扫最后一个座位。所以best[i][j] = max(best[i-1][j/2],best[i-1][j/2 + (1<<(M-1)) )。若果j为奇数,best[i][j]还要再加上weight[i]。
3)如果one_num(j)等于Q时,与小于Q时一样
4、当然计算best第i行时只用到了第i-1行的数据,可以用2*(1《M)大小的二维数组。至于为什么不能像前面的例题一样用一维数组,是因为这里best[i][j] = max(best[i-1][j/2],best[i-1][j/2 + (1<<(M-1)) ),不能保证用于更新的数据(j/2,j/2+(1<<(M-1))都在待更新的数据(j)的一边。
可执行代码
#include<iostream>
#include<fstream>
#include<math.h>
using namespace std;
int ones(int j)
{
int res = 0;
while(j!=0)
{
res+=(j%2);
j/=2;
}
return res;
}
int main()
{
ifstream cin("input.txt");
int N,M,Q;
cin>>N>>M>>Q;
//垃圾数
int * weight = new int[N];
for(int i =0;i<N;i++)
cin>>weight[i];
//动态数组best(i,j)表示在j决策下扫到第i个座位能清理的最多垃圾
int ** best = new int*[N+1];
int len = 1<<M;
for(int i = 0;i<=N;i++)
{
best[i] = new int[len];
//memset(best[i],0,len*sizeof(best[i]));
for(int j = 0;j<len;j++)
best[i][j] = 0;
}
for(int i = 0;i<N+1;i++)
{
for(int j = 0;j<len;j++)
cout<<best[i][j]<<" ";
cout<<endl;
}
for(int i = 0;i<N;i++)
{
for(int j =0;j<len;j+=2)
{
int one_num = ones(j);
if(one_num>Q)
best[i+1][j] = best[i+1][j+1] = 0;
if(one_num<Q)
{
int temp;
int a = j>>1;
int b = a + (1<<(M-1));//j可由上一层的a和b转移过来
temp = max(best[i][a],best[i][b]);
best[i+1][j] = best[i+1][j+1] = temp;
best[i+1][j+1] += weight[i];
}
if(one_num == Q)
{
int temp;
int a = j>>1;
int b = a + (1<<(M-1));//j可由上一层的a和b转移过来
temp = max(best[i][a],best[i][b]);
best[i+1][j] = temp;
best[i+1][j+1] = 0;
}
}
}
test
//for(int i = 0;i<=N;i++)
//{
// for(int j = 0;j<len;j++)
// cout<<best[i][j]<<" ";
// cout<<endl;
//}
//输出最后一行的最大数
int result = 0;
for(int i = 0;i<len;i++)
result = max(best[N][i],result);
cout<<result;
for(int i = 0;i<=N;i++)
delete[] best[i];
delete[] best;
delete[] weight;
return 0;
}