题解:P10961 划分大理石
题目传送门
这道题是不错的背包练习题,推荐像我这样的蒟蒻来写。
题意很明白了,在此不赘述。
思路讲解
首先一个特判:当价值总和不是偶数,直接 cout<<"Can't"。
通过观察,可以很快发现这是一道背包 DP 题,而且还是 01 背包的变式——多重背包,不过与常规模板不同的是,这里只需要判断是否可以取到即可,所以我们可以将
考虑转移,可以发现,
而初始状态就是
My code
#include<bits/stdc++.h>
using namespace std;
int a[10],sum,num,w[120005],id;
bool dp[120005];
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
while(true){
sum=0;
for(int i=1;i<=6;cin>>a[i],sum+=a[i]*i,i++);//计算价值总和
num=sum>>1,id=0;//计算出要得到的价值,因为只要总价值的一半可以取到,另一半也一定可以取得到。
fill(dp,dp+120005,false);//初始化。
if(sum==0)break;//若价值为0,即输入均为0。
if(sum&1)//特判。
cout<<"Can't\n";
else{
for(int i=1;i<=6;i++)
for(int j=1;j<=a[i];j++)w[++id]=i;//预处理w数组。
dp[0]=true;//初始状态。
for(int i=1;i<=id;i++)
for(int j=num;j>=w[i];j--)
dp[j]|=dp[j-w[i]];//背包模板,转移使用|运算(或)。
if(dp[num])cout<<"Can\n";
else cout<<"Can't\n";//输出。
}
}
return 0;//华丽结束。
}
但是,当你提交上去之后会惊讶的发现:
Subtask 2 全 TLE 了!!
那怎么办呢?这时候就要用二进制优化大法了!!(dalao 们可以用状态压缩。)
优化原理:
我们知道,任意一个数都可以表示为二进制数。
那换句话说,是不是二进制可以构成任意数?
所以,我们可以将一个数使用二进制各位权值之和表示,存进
AC code
#include<bits/stdc++.h>
using namespace std;
int a[10],sum,num,w[120005],id;
bool dp[120005];
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
while(true){
sum=0;
fill(dp,dp+120005,false);
for(int i=1;i<=6;cin>>a[i],sum+=a[i]*i,i++);
if(sum==0)break;
num=sum>>1,id=0;
if(sum&1){
cout<<"Can't\n";
continue;
}
for(int i=1;i<=6;i++){
int temp=0;
for(int j=1;(j<<1)<=a[i];j<<=1)w[++id]=i*j,temp+=j;//二进制优化。
w[++id]=i*(a[i]-temp);//万一有剩余的,再存进去。
}
dp[0]=true;
for(int i=1;i<=id;i++)
for(int j=num;j>=w[i];j--)
dp[j]|=dp[j-w[i]];
if(dp[num])cout<<"Can\n";
else cout<<"Can't\n";
}//其他同上。
return 0;
}