01背包
P1048 采药
题目描述
辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”
如果你是辰辰,你能完成这个任务吗?
输入格式
第一行有 222 个整数 TTT(1≤T≤10001 \le T \le 10001≤T≤1000)和 MMM(1≤M≤1001 \le M \le 1001≤M≤100),用一个空格隔开,TTT 代表总共能够用来采药的时间,MMM 代表山洞里的草药的数目。
接下来的 MMM 行每行包括两个在 111 到 100100100 之间(包括 111 和 100100100)的整数,分别表示采摘某株草药的时间和这株草药的价值。
输出格式
输出在规定的时间内可以采到的草药的最大总价值。
输入输出样例
输入 #1
70 3
71 100
69 1
1 2
输出 #1
3
是一个01背包问题
注意的是,01背包问题的话,第二层循环要从容量开始到这个物品的容量结束,
因为01背包是从上一层的状态转移过来的
#include<iostream>
#include<cstring>
using namespace std;
int t[1234],w[1234];
int dp[1234];
int main()
{
int tt,ww;
cin>>tt>>ww;
for(int i=0;i<ww;i++) cin>>t[i]>>w[i];
for(int i=0;i<ww;i++)
{
for(int j=tt;j>=t[i];j--)
{
dp[j]=max(dp[j],dp[j-t[i]]+w[i]);
}
}
cout<<dp[tt]<<endl;
return 0;
}
P1060 开心的金明
题目描述
金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间他自己专用的很宽敞的房间。更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过NNN元钱就行”。今天一早金明就开始做预算,但是他想买的东西太多了,肯定会超过妈妈限定的NNN元。于是,他把每件物品规定了一个重要度,分为555等:用整数1−51-51−5表示,第555等最重要。他还从因特网上查到了每件物品的价格(都是整数元)。他希望在不超过NNN元(可以等于NNN元)的前提下,使每件物品的价格与重要度的乘积的总和最大。
设第jjj件物品的价格为v[j]v_[j]v[j],重要度为w[j]w_[j]w[j],共选中了kkk件物品,编号依次为j1,j2,…,jkj_1,j_2,…,j_kj1,j2,…,jk,则所求的总和为:v[j1]×w[j1]+v[j2]×w[j2]+…+v[jk]×w[jk]v_[j_1] \times w_[j_1]+v_[j_2] \times w_[j_2]+ …+v_[j_k] \times w_[j_k]v[j1]×w[j1]+v[j2]×w[j2]+…+v[jk]×w[jk]。
请你帮助金明设计一个满足要求的购物单。
输入格式
第一行,为222个正整数,用一个空格隔开:nmn mnm(其中N(<30000)N(<30000)N(<30000)表示总钱数,m(<25)m(<25)m(<25)为希望购买物品的个数。)
从第222行到第m+1m+1m+1行,第jjj行给出了编号为j−1j-1j−1的物品的基本数据,每行有222个非负整数vp v pvp(其中vvv表示该物品的价格(v≤10000)(v \le 10000)(v≤10000),ppp表示该物品的重要度(1−51-51−5)
输出格式
111个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值(<100000000)(<100000000)(<100000000)。
输入输出样例
输入 #1
1000 5
800 2
400 5
300 5
400 3
200 2
输出 #1
3900
这个也是一个01背包问题,但是要注意每一个物品的价值
#include<iostream>
#include<cstring>
using namespace std;
int t[987234],w[987234];
int dp[987234];
int main()
{
int tt,ww;
cin>>tt>>ww;
for(int i=0;i<ww;i++)
{
cin>>t[i]>>w[i];
w[i]=t[i]*w[i];
}
for(int i=0;i<ww;i++)
{
for(int j=tt;j>=t[i];j--)
{
dp[j]=max(dp[j],dp[j-t[i]]+w[i]);
}
}
cout<<dp[tt]<<endl;
return 0;
}
P1049 装箱问题
题目描述
有一个箱子容量为VVV(正整数,0≤V≤200000 \le V \le 200000≤V≤20000),同时有nnn个物品(0<n≤300<n \le 300<n≤30,每个物品有一个体积(正整数)。
要求nnn个物品中,任取若干个装入箱内,使箱子的剩余空间为最小。
输入格式
111个整数,表示箱子容量
111个整数,表示有nnn个物品
接下来nnn行,分别表示这nnn个物品的各自体积
输出格式
111个整数,表示箱子剩余空间。
输入输出样例
输入 #1
24
6
8
3
12
7
9
7
输出 #1
0
01背包问题,但是让求的是剩下的容量,所以最后要减一下
#include<iostream>
#include<cstring>
using namespace std;
int v[72345],w[72345];
int dp[72345];
int main()
{
int vv,ww;
cin>>vv>>ww;
for(int i=0;i<ww;i++)
{
cin>>v[i];
w[i]=v[i];
}
for(int i=0;i<ww;i++)
{
for(int j=vv;j>=v[i];j--)
{
dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
}
}
cout<<vv-dp[vv]<<endl;
return 0;
}
P1802 5倍经验日
题目背景
现在乐斗有活动了!每打一个人可以获得5倍经验!absi2011却无奈的看着那一些比他等级高的好友,想着能否把他们干掉。干掉能拿不少经验的。
题目描述
现在absi2011拿出了x个迷你装药物(嗑药打人可耻….),准备开始与那些人打了 由于迷你装一个只能管一次,所以absi2011要谨慎的使用这些药,悲剧的是,没到达最少打败该人所用的属性药了他打人必输>.<所以他用2个药去打别人,别人却表明3个药才能打过,那么相当于你输了并且这两个属性药浪费了。
现在有n个好友,有输掉拿的经验、赢了拿的经验、要嗑几个药才能打过。求出最大经验(注意,最后要乘以5)
输入格式
第一行两个数,n和x 后面n行每行三个数,分别表示输了拿到的经验(lose[i])、赢了拿到的经验(win[i])、打过要至少使用的药数量(use[i])。
输出格式
一个整数,最多获得的经验
输入输出样例
输入 #1
6 8
21 52 1
21 70 5
21 48 2
14 38 3
14 36 1
14 36 2
输出 #1
1060
话说这个题不能把不嗑药的当作0,然后是2n个背包,因为这样可能一个人被打了两次
#include<iostream>
#include<cstring>
using namespace std;
int vv[12345][2],ww[12454];
long long dp[123445];
int main()
{
int n,m;
cin>>n>>m;
for(int i=0;i<n;i++) cin>>vv[i][0]>>vv[i][1]>>ww[i];
for(int i=0;i<n;i++)
{
for(int j=m;j>=0;j--)
{//如果大于所需要的药的量,就判断是嗑药拿赢的经验好,还是不嗑药拿输的经验好
if(j>=ww[i]) dp[j]=max(dp[j]+vv[i][0],dp[j-ww[i]]+vv[i][1]);
else dp[j]=dp[j]+vv[i][0];//如果小于,那就只能那输的经验了
}
}
cout<<dp[m]*5<<endl;
return 0;
}
P1507 NASA的食物计划
题目背景
NASA(美国航空航天局)因为航天飞机的隔热瓦等其他安全技术问题一直大伤脑筋,因此在各方压力下终止了航天飞机的历史,但是此类事情会不会在以后发生,谁也无法保证,在遇到这类航天问题时,解决方法也许只能让航天员出仓维修,但是多次的维修会消耗航天员大量的能量,因此NASA便想设计一种食品方案,让体积和承重有限的条件下多装载一些高卡路里的食物.
题目描述
航天飞机的体积有限,当然如果载过重的物品,燃料会浪费很多钱,每件食品都有各自的体积、质量以及所含卡路里,在告诉你体积和质量的最大值的情况下,请输出能达到的食品方案所含卡路里的最大值,当然每个食品只能使用一次.
输入格式
第一行 两个数 体积最大值(<400)和质量最大值(<400)
第二行 一个数 食品总数N(<50).
第三行-第3+N行
每行三个数 体积(<400) 质量(<400) 所含卡路里(<500)
输出格式
一个数 所能达到的最大卡路里(int范围内)
输入输出样例
输入 #1
320 350
4
160 40 120
80 110 240
220 70 310
40 400 220
输出 #1
550
这个题要三重循环了
第一层循环n个物品,第二层循环体积,第三重循环质量,
因为是每件物品只能用一次,所以是一个01背包问题,所以第二层,第三层循环的时候要从最大容量开始循环
#include<iostream>
#include<cstring>
using namespace std;
int v[1234],w[1234],c[1234];
int dp[1234][1234];
int main()
{
int maxv,maxw,n;
cin>>maxv>>maxw>>n;
for(int i=0;i<n;i++)
cin>>v[i]>>w[i]>>c[i];
for(int i=0;i<n;i++)
{
for(int j=maxv;j>=v[i];j--)
{
for(int k=maxw;k>=w[i];k--)
dp[j][k]=max(dp[j][k],dp[j-v[i]][k-w[i]]+c[i]);
}
}
cout<<dp[maxv][maxw]<<endl;
return 0;
}
分组背包
P1757 通天之分组背包
题目背景
直达通天路·小A历险记第二篇
题目描述
自01背包问世之后,小A对此深感兴趣。一天,小A去远游,却发现他的背包不同于01背包,他的物品大致可分为k组,每组中的物品相互冲突,现在,他想知道最大的利用价值是多少。
输入格式
两个数m,n,表示一共有n件物品,总重量为m
接下来n行,每行3个数ai,bi,ci,表示物品的重量,利用价值,所属组数
输出格式
一个数,最大的利用价值
输入输出样例
输入 #1
45 3
10 10 1
10 5 1
50 400 2
输出 #1
10
分组背包,也是要三重循环,
第一层循环组数,第二层循环容量,第三重循环每一组的每一个物品
但是分组背包里第二重循环的时候要从m到0;
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
vector<int> vv[1234],ww[1234];
int dp[1234];
int t;
int main()
{
int n,m;
cin>>m>>n;
for(int i=0;i<n;i++)
{
int v,w,s;
cin>>v>>w>>s;
t=max(t,s);
vv[s].push_back(v);ww[s].push_back(w);
}
for(int i=1;i<=t;i++)
{
for(int j=m;j>=0;j--)
{
for(int k=0;k<vv[i].size();k++)
if(j>=vv[i][k]) dp[j]=max(dp[j],dp[j-vv[i][k]]+ww[i][k]);
}
}
cout<<dp[m]<<endl;
return 0;
}
AcWing 9. 分组背包问题
有 N 组物品和一个容量是 V的背包。
每组物品有若干个,同一组内的物品最多只能选一个。
每件物品的体积是 vij,价值是 wij,其中 i 是组号,j是组内编号。
求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。
输出最大价值。
输入格式
第一行有两个整数 N,V,用空格隔开,分别表示物品组数和背包容量。
接下来有 N组数据: 每组数据第一行有一个整数 Si,表示第 i个物品组的物品数量;
每组数据接下来有 Si行,每行有两个整数 vij,wij,用空格隔开,分别表示第 i 个物品组的第 j 个物品的体积和价值;
输出格式
输出一个整数,表示最大价值。
数据范围
0<N,V≤100
0<Si≤100
0<vij,wij≤100
输入样例
3 5
2
1 2
2 4
1
3 4
1
4 5
输出样例:
8
#include<iostream>
#include<cstring>
using namespace std;
int vv[12345],ww[12345];
int dp[12345];
int main()
{
int n,m;
cin>>n>>m;
for(int i=0;i<n;i++)
{
int s;
cin>>s;
for(int j=0;j<s;j++) cin>>vv[j]>>ww[j];
for(int j=m;j>=0;j--)
{
for(int k=0;k<s;k++)
if(j>=vv[k]) dp[j]=max(dp[j],dp[j-vv[k]]+ww[k]);
}
}
cout<<dp[m]<<endl;
return 0;
}
完全背包
AcWing 3. 完全背包问题
有 N 种物品和一个容量是 V的背包,每种物品都有无限件可用。
第 i种物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。
输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。
接下来有 N行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 种物品的体积和价值。
输出格式
输出一个整数,表示最大价值。
数据范围
0<N,V≤1000
0<vi,wi≤1000
输入样例
4 5
1 2
2 4
3 4
4 5
输出样例:
10
完全背包与01背包类似,转移方程时只有第二重循环不一样,
完全背包问题,每一个物品都有无限次可以选择,
第二重循环时应该从v[i]循环到m
#include<iostream>
#include<cstring>
using namespace std;
int v[12345],w[12345];
int dp[12345];
int main()
{
int n,maxv;
cin>>n>>maxv;
for(int i=0;i<n;i++) cin>>v[i]>>w[i];
for(int i=0;i<n;i++)
{
for(int j=v[i];j<=maxv;j++)
dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
}
cout<<dp[maxv]<<endl;
return 0;
}
P1832 A+B Problem 【完全背包+计数】
题目背景
·题目名称是吸引你点进来的
·实际上该题还是很水的
题目描述
·1+1=? 显然是2
·a+b=? 1001回看不谢
·哥德巴赫猜想 似乎已呈泛滥趋势
·以上纯属个人吐槽
·给定一个正整数n,求将其分解成若干个素数之和的方案总数。
输入格式
一行:一个正整数n
输出格式
一行:一个整数表示方案总数
输入输出样例
输入 #1
7
输出 #1
3
这个题想起了强哥的话,从0个数字中选择n个方法是1,这是存在一种方法的,是方法数,并不是结果
所以这个题的dp[0]=1;
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
bool st[1234];
int primes[1245];
int cnt;
long long dp[12345];
void get_prime(int n)
{
for(int i=2;i<=n;i++)
{
if(!st[i])
{
primes[cnt++]=i;
for(int j=i+i;j<=n;j+=i) st[j]=1;
}
}
}
int main()
{
int n;
cin>>n;
get_prime(n);
dp[0]=1;
for(int i=0;i<cnt;i++)
{
for(int j=primes[i];j<=n;j++)
{
dp[j]=dp[j]+dp[j-primes[i]];
}
}
cout<<dp[n]<<endl;
return 0;
}
多重背包
AcWing 5. 多重背包问题 II
有 N 种物品和一个容量是 V的背包。
第 i种物品最多有 si 件,每件体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。
输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。
接下来有 N行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i 种物品的体积、价值和数量。
输出格式
输出一个整数,表示最大价值。
数据范围
0<N≤1000
0<V≤2000
0<vi,wi,si≤2000
提示:
本题考查多重背包的二进制优化方法。
输入样例
4 5
1 2 3
2 4 1
3 4 3
4 5 2
输出样例:
10
这个题用二进制将每个物品的件数分解之后,就可以直接做一遍01背包就行了
#include<iostream>
#include<cstring>
using namespace std;
const int N=1e5+7;
int vv[N],ww[N],dp[N];
int main()
{
int n,m;
int cnt=0;
cin>>n>>m;
while(n--)
{
int v,w,s;int x=1;
cin>>v>>w>>s;
while(x<=s)
{
vv[cnt]=v*x;
ww[cnt]=w*x;
cnt++;
s-=x;
x=x*2;
}
if(s)
{
vv[cnt]=v*s;
ww[cnt]=w*s;
cnt++;
}
}
for(int i=0;i<cnt;i++)
{
for(int j=m;j>=vv[i];j--)
dp[j]=max(dp[j],dp[j-vv[i]]+ww[i]);
}
cout<<dp[m]<<endl;
return 0;
}
01背包
每个物品只能选一次,
第二重循环要从容量m循环到v[i];
完全背包
每个物品都有无限多个,
第二重循环要从v[i]循环到容量m
多重背包
每个背包都有s个,
先把s用二进制分解,然后再把分解之后的背包做一遍01背包
分组背包
每个组里的物品是互斥的,在同一组里选了这个就不能选其他的
三重循环,第一层循环组数,第二重循环容量,从容量m开始循环到0,第三重循环每一组的每一个物品
然后判断如果当前容量大于物品的体积就更新