P4141 消失之物 题解
题意
给你
思路
不知道为什么没有人用分治(
对于一段区间
因为每一次我们都对没做背包的部分的一半又做了背包,又因为背包答案与顺序无关,所以
时间复杂度是分治加背包,
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
namespace acac
{
int dp[2010][2010];
int Ans[2010][2010];
int W[2010];
int n,m;
const int mod=10;
void sovle(int dep,int l,int r)//分治,dep是为了方便处理现在(l,r)的背包
{
if(l==r)//背包正好有一个没处理
{
for(int i=1;i<=m;i++)
{
Ans[l][i]=dp[dep-1][i];
}
return ;
}
int mid=(l+r)>>1;
for(int i=0;i<=m;i++)
{
dp[dep][i]=dp[dep-1][i];
}
for(int i=l;i<=mid;i++)//处理左半边
{
for(int j=m;j>=W[i];j--)
{
// if(dep==1)cout<<j-W[i]<<' '<<dp[dep][j-W[i]]<<endl;
dp[dep][j]=(dp[dep][j]+dp[dep][j-W[i]])%mod;
}
}
sovle(dep+1,mid+1,r);//去还未处理的右半边
for(int i=0;i<=m;i++)//重置,不然左边会算进来
{
dp[dep][i]=dp[dep-1][i];
}
for(int i=mid+1;i<=r;i++)//处理右半边
{
for(int j=m;j>=W[i];j--)
{
dp[dep][j]=(dp[dep][j]+dp[dep][j-W[i]])%mod;
}
}
sovle(dep+1,l,mid);//去还未处理的左半边
}
int main()
{
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%lld",&W[i]);
}
dp[0][0]=1;
sovle(1,1,n);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
cout<<Ans[i][j];
}
cout<<'\n';
}
return 0;
}
}
#undef int
int main()
{
acac::main();
return 0;
}
其他
为什么这题可以暴力回退?是因为这题为方案数,总答案一定会算删去物品的贡献,但是如果像 01背包一样不一定算删去物品的贡献就会汗流浃背。而这种方法就很好规避了这种问题。 还可以避免减法取模 。
不妨试试!