P1048 采药
灵魂发问:如果你是辰辰,你能完成这个任务吗?
答案是:还是不太难的
这一题很基础一个01背包,属于纯纯板子题,输入输出弄好了上直接状态转移方程即可,下面是优化成一维数组的代码
就是要注意:在这里需要 j 反着遍历,不然就有可能会有重复的情况,反着来的话,前面的情况都是0,就不会出现一个物品被选两次的情况啦
代码如下:
#include<bits/stdc++.h>
using namespace std;
int m,t;
const int N=1e3+5;
int dp[N];
int ti[N],va[N];
int main()
{
cin>>t>>m;
for(int i=1;i<=m;i++)
cin>>ti[i]>>va[i];
for(int i=m;i>=1;i--)
{
for(int j=t;j>=ti[i];j--)
dp[j]=max(dp[j],dp[j-ti[i]]+va[i]);
}
cout<<dp[t];
return 0;
}
P1616 疯狂的采药
对于这一题只能说大无语,是个完全背包的板子题啦
如果你是 LiYuxiang,你能完成这个任务吗?
也可以捏
和01背包有所不同的是,完全背包需要从头开始,就不能反着遍历了,然后记得开long long,不然一切白搭,然后。就没有然后了,跟01一模一样了
代码如下:
#include<bits/stdc++.h>
using namespace std;
long long m,t;
const int N=1e4+5,M=1e7+5;
long long dp[M];
long long ti[N],va[N];
int main()
{
cin>>t>>m;
for(int i=1;i<=m;i++)
cin>>ti[i]>>va[i];
for(int i=1;i<=m;i++)
{
for(int j=ti[i];j<=t;j++)
dp[j]=max(dp[j],dp[j-ti[i]]+va[i]);
}
cout<<dp[t];
return 0;
}
P1049 装箱问题
01背包,不多说了
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=2e4+5,M=35;
int dp[M][N];
int main()
{
int v,n;
memset(dp,0,sizeof(dp));
cin>>v>>n;
int vv[M];
for(int i=1;i<=n;i++)
cin>>vv[i];
for(int i=1;i<=n;i++)
{
for(int j=v;j>=0;j--)
{
if(j>=vv[i])
dp[i][j]=max(dp[i-1][j],dp[i-1][j-vv[i]]+vv[i]);
else
dp[i][j]=dp[i-1][j];
}
}
cout<<v-dp[n][v];
return 0;
}
P1833 樱花
这题综合性挺强的,01背包,完全背包,和多重背包凑一块了,害,没事啦没事啦,两种思路
一种是全部转化成01背包:完全背包次数设为999999,再和多重背包一起进行二进制转化,最后都变成01背包模型
第二种是单单多重背包转化成01背包,完全背包在判断出来同时直接进行dp,单设数组存多重背包和01后,再对其进行一次dp,然后就ok啦
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5,M=1e3+5;
int ti[N],co[N];
int dp[N];
int main()
{
//t为看完第i棵树所需时间,c为价值,p为次数
int h,hh,m,mm,t,c,p,n;
char a;
int cnt=1;
cin>>h>>a>>m>>hh>>a>>mm>>n;
int tt=(hh-h)*60+mm-m;
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)
{
cin>>t>>c>>p;
if(p==0)
{
for(int j=t;j<=tt;j++)
dp[j]=max(dp[j],dp[j-t]+c);
}
int q=1;
while(p>=q)
{
co[cnt]=q*c;
ti[cnt++]=q*t;
p-=q;
q<<=1;
}
if(p)
{
co[cnt]=p*c;
ti[cnt++]=p*t;
}
}
for(int i=1;i<=cnt;i++)
for(int j=tt;j>=ti[i];j--)
{
dp[j]=max(dp[j],dp[j-ti[i]]+co[i]);
}
cout<<dp[tt];
}
以下是选做题:
P1077 摆花
定义状态:dp[i][j]表示前 i 种花总和为 j 的方案数
可以看出来需要有三个循环,一个是遍历花种数 i,一个用来遍历从0到最大摆放数 j,最里面的用来遍历每种花的数量 k,可以推出:知道了第 i-1 种前的所有种类的花要摆放的数量,那么对于第 i 种花需要的数量就需要加上 i-1 种花的种类数,同时减去摆放的 k 盆花,那么可以得到如下状态转移方程:
代码送上:
#include<bits/stdc++.h>
using namespace std;
int n,m;
int md=1e6+7;
int a[105];
int dp[105][105];
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>a[i];
dp[0][0]=1;
for(int i=1;i<=n;i++)
for(int j=0;j<=m;j++)
for(int k=0;k<=min(a[i],j);k++)
dp[i][j]=(dp[i][j]+dp[i-1][j-k])%md;
cout<<dp[n][m];
}
P1064 金明的预算方案
救命,乍一看这题,以为就是个简简单单的背包,可是理解了题目意思后却发现,暗藏玄机啊。。。好好的物件,居然还分成了主件和附件,那么就有待考究了。
对于主件附件,直接想法是把主件和附件分类,可是还需要让每个附件对应上每个主件,并且想要购买附件就必须要买主件,不过好在附件只会有0,1,2个,那就好办啦,把分成4种情况:
1、只考虑主件
2、考虑主件以及每个主件的第一个附件
3、考虑主件以及每个主件的第二个附件
4、考虑主件以及所有的附件(最多两个)
因此,问题就转化成很板的背包问题啦
状态转移方程:
dp[j]=max(dp[j],dp[j-zjg[i]]+zzy[i]);
if(j>=zjg[i]+fjg[i][1])
dp[j]=max(dp[j],dp[j-zjg[i]-fjg[i][1]]+zzy[i]+fzy[i][1]);
if(j>=zjg[i]+fjg[i][2])
dp[j]=max(dp[j],dp[j-zjg[i]-fjg[i][2]]+zzy[i]+fzy[i][2]);
if(j>=zjg[i]+fjg[i][1]+fjg[i][2])
dp[j]=max(dp[j],dp[j-zjg[i]-fjg[i][1]-fjg[i][2]]+zzy[i]+fzy[i][1]+fzy[i][2]);
代码如下:
#include<bits/stdc++.h>
using namespace std;
int n,m;
const int N=1e5+5;
int v,w,p,f=0;
int dp[N],zjg[N],zzy[N],fjg[N][3],fzy[N][3];
int maxx=0;
int main()
{
cin>>n>>m;
for(int i=1;i<=m;i++)
{
cin>>v>>w>>p;
if(p==0)
zjg[i]=v,zzy[i]=v*w;
else
fjg[p][0]++,fjg[p][fjg[p][0]]=v,fzy[p][fjg[p][0]]=v*w;
}
for(int i=1;i<=m;i++)
for(int j=n;j>=zjg[i];j--)
{
dp[j]=max(dp[j],dp[j-zjg[i]]+zzy[i]);
if(j>=zjg[i]+fjg[i][1])
dp[j]=max(dp[j],dp[j-zjg[i]-fjg[i][1]]+zzy[i]+fzy[i][1]);
if(j>=zjg[i]+fjg[i][2])
dp[j]=max(dp[j],dp[j-zjg[i]-fjg[i][2]]+zzy[i]+fzy[i][2]);
if(j>=zjg[i]+fjg[i][1]+fjg[i][2])
dp[j]=max(dp[j],dp[j-zjg[i]-fjg[i][1]-fjg[i][2]]+zzy[i]+fzy[i][1]+fzy[i][2]);
}
cout<<dp[n];
return 0;
}
P8742 砝码称重
这题真的想不出来一点,特别是它的加上一个砝码,后来又要减去一个砝码,特别难实现的感觉,不过这题又给我带来了一个新思路
将总重量逐渐减小,并遍历下去,分成四种情况:
1、目前重量是否等于某个砝码单独的重量;
2、不取第 i 个砝码,判断是否有过该重量;
3、取第 i 个砝码,加到同侧,判断拿 i 个砝码是否达到过此重量;
4、取第 i 个砝码,加到对侧,即目前重量减去砝码重的绝对值,判断拿 i 个砝码是否达到过此重量
如果有这些情况发生,那么对于dp就赋予1,意为拿 i 个砝码达到重量 j 的情况有过了,当然了,最后要求的是使用 n 个砝码,于是就有了最后一波遍历
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int N=105,M=1e8+5;
long long dp[101][100001];
long long pp[M];
int n;
int ans;
int w[N];
int main()
{
cin>>n;
int ww=0;
for(int i=1;i<=n;i++)
{
cin>>w[i];
ww+=w[i];
}
for(int i=1;i<=n;i++)
for(int j=ww;j;j--)
{
if(j==w[i])
dp[i][j]=1;
else if(dp[i-1][j])
dp[i][j]=1;
else if(dp[i-1][j+w[i]])
dp[i][j]=1;
else if (dp[i-1][abs(j-w[i])])
dp[i][j]=1;
}
for(int i=1;i<=ww;i++)
if(dp[n][i])
ans++;
cout<<ans;
return 0;
}
P1855 榨取kkksc03
本题属于多重背包的板子题,有时间和金钱两条限制,于是很容易能得到两个状态:时间剩余和金钱剩余,再根据01背包的模版,那么状态转移方程即为:
代码如下:
#include<bits/stdc++.h>
using namespace std;
int n,m,t;//n个愿望,剩余m元,剩余t分钟
const int N=205;
int dp[N][N];
int mm[N],tt[N];
int maxx;
int main()
{
cin>>n>>m>>t;
for(int i=1;i<=n;i++)
cin>>mm[i]>>tt[i];
for(int i=1;i<=n;i++)
for(int j=m;j>=mm[i];j--)
for(int k=t;k>=tt[i];k--)
dp[j][k]=max(dp[j][k],dp[j-mm[i]][k-tt[i]]+1);
cout<<dp[m][t];
return 0;
}
P2946 Cow Frisbee Team S
这题属于是比较奇怪的一题了,从头到尾所有字无一不在呐喊:我是背包,可是问的问题却是要求种类数,很奇葩,无从下手
那么就换个角度,既然要达到F的倍数才算,那么就这样定义状态:
表示选 i 头牛,各个数之和为 j 时,所有的方案数,那么就有几种情况:
1、已经选了 i 头牛,但是没有选第 i 头,应在前 i−1 头牛中取来若干和为 j 的数即
2、选取第 i 头,则应在前 i−1 头牛中取来若干和为 j−r[i] 的数这样最后才是 j 头牛
但是要时刻记得mod!!
那么状态转移方程也就呼之欲出啦:
(由于格式问题,此处没有%),那么代码如下:
#include<bits/stdc++.h>
using namespace std;
int n,f;
int md=1e8;
const int N=2e3+5;
int r[N];
int dp[N][N];
int main()
{
cin>>n>>f;
for(int i=1;i<=n;i++)
{
cin>>r[i];
r[i]%=f;
}
for(int i=1;i<=n;i++)
dp[i][r[i]]=1;
for(int i=1;i<=n;i++)
for(int j=0;j<f;j++)
dp[i][j]=dp[i][j]%md+dp[i-1][j]%md+dp[i-1][(j-r[i]+f)%f]%md,dp[i][j]%=md;
cout<<dp[n][0];
return 0;
}