消失之物
传送门:
P4141 消失之物 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
思路
暴力稳了
但是
hack
tle了
这时候我们要想办法优化
这是一个回退背包问题
首先第一步,我们把正常的背包(n间物体)求出来,然后就是板子,求出填满当中体积有多少种方法
第二步就是回退,
回退的关键问题有两个:
- 回退的点该怎么找
- 只把由当前物品组成的情况删除
解决这两个问题就是会了回退,会了回退就是解决问题了
这两个关键点分两步解决:
-
当前背包容量小于我需要回退的物品时,说明我当前压根没有带走该物体,所以当前的刚好填满的方法数量还是不变
-
当我背包容量大于等于我需要回退的物品时,说明我当前正好可以填满的方法中有部分方法是由本该退回的物品组成的,至少为1(因为当前背包容量等于当前物品体积的时候,这是肯定为一种情况的)
-
第二点用以下代码解决
f[j]=(dp[j]-f[j-a[i]]+10)%10;//dp表示原来n件物品的背包,f表示拿掉第i件的背包恰好填满的情况 //原方法总数-由退回物品组成的方法总数 //为什么f[j-a[i]]是退回物品的方法总数呢? //因为a[i]与j-a[i]才能恰好组成j也就是当前背包容量,一件a[i]与n件j-a[i]可以恰好组成n种方法 //还有个问题,其实我一开始在思考这里回退j-a[i]的时候会不会把之前已经回退过的二次回退导致答案变小呢 //事实是不会的,完全多想 //因为每次只考虑一个物品所以不搭嘎,因为这里的关系是关联关系,j-a[i]的方法总数是影响a[i]的方法总数,所以这边就算收到影响也是因为j-a[i]受到了影响,那么j-a[i]受到影响这个方法总数肯定是会受到影响的。 //这里有点绕,有兴趣可以思考一下后半句,初学者只要牢记前半句就行;
-
tips:这里的mod 10在每次计算的时候就可以做了,建议求出来的时候先+10再取模,有情况下是看你去负的。
上面的问题都是本人自己写题思考看题解学习,写给每一位跟我一样新入门的同学看!
有问题请评论或私信我,谢谢!
AC code
// Problem:
// P4141 消失之物
//
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P4141
// Memory Limit: 256 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<iostream>
#include<algorithm>
//#include<cstdio>
#include<cstring>
#define ll long long
#define endl '\n'
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define N 2100 //1e6+100
using namespace std;
int n,m,a[N],dp[N],f[N];
int main(){
//ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>m;
rep(i,1,n) cin>>a[i];
dp[0]=1;
rep(i,1,n){
per(j,m,a[i]){
dp[j]+=dp[j-a[i]];
dp[j]%=10;
}
}
rep(i,1,n){
f[0]=1;
rep(j,1,m){
if(j<a[i])f[j]=dp[j]%10;
else f[j]=(dp[j]-f[j-a[i]]+10)%10;
cout<<f[j];
}
cout<<endl;
}
return 0;
}
标签:10,背包,题解,之物,回退,P4141,dp
From: https://www.cnblogs.com/Illuminated/p/18024208