kkksc03考前临时抱佛脚
题目背景
kkksc03 的大学生活非常的颓废,平时根本不学习。但是,临近期末考试,他必须要开始抱佛脚,以求不挂科。
题目描述
这次期末考试,kkksc03 需要考 4 科。因此要开始刷习题集,每科都有一个习题集,分别有 s1,s2,s3,s4 道题目,完成每道题目需要一些时间,可能不等(A1,A2,…,As1,B1,B2,…,Bs2,C1,C2,…,Cs3,D1,D2,…,Ds4)。
kkksc03 有一个能力,他的左右两个大脑可以同时计算 2 道不同的题目,但是仅限于同一科。因此,kkksc03 必须一科一科的复习。
由于 kkksc03 还急着去处理洛谷的 bug,因此他希望尽快把事情做完,所以他希望知道能够完成复习的最短时间。
输入格式
本题包含 5 行数据:第 1 行,为四个正整数 s1,s2,s3,s4。
第 2 行,为 A1,A2,…,As1 共 s1 个数,表示第一科习题集每道题目所消耗的时间。
第 3 行,为 B1,B2,…,Bs2 共 s2 个数。
第 4 行,为 C1,C2,…,Cs3 共 s3 个数。
第 5 行,为 D1,D2,…,Ds4 共 s4 个数,意思均同上。
输出格式
输出一行,为复习完毕最短时间。
样例 #1
样例输入 #1
样例输出 #1
提示
1≤s1,s2,s3,s4≤20。
1≤A1,A2,…,As1,B1,B2,…,Bs2,C1,C2,…,Cs3,D1,D2,…,Ds4≤60。
本题为背包dp的 “ 简单 ” 变式:虽然简单变一变本蒟蒻已经不会了。
一开始的思路是贪心算法:对每一组而言先排序,然后从后向前把元素往两个空脑子里丢,哪个脑子目前内容比较少就丢哪个。
代码如下,喜提零分(听取蛙声一片)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| #include <iostream> using namespace std; #include <algorithm> #include <queue> const int maxsize = 25;
int n, inp; int min_time(int a[],int n) { if(n==1)return a[0]; int lsum=0,rsum=0; sort(a,a+n); for(int i=n-1;i>=0;i--) { if(lsum>rsum)rsum+=a[i]; else lsum+=a[i]; } return max(lsum,rsum); } int main() { int sum_time=0,na,nb,nc,nd; int a[maxsize],b[maxsize],c[maxsize],d[maxsize]; cin>>na>>nb>>nc>>nd;
for(int i=0;i<na;i++) cin>>a[i]; for(int i=0;i<nb;i++) cin>>b[i]; for (int i = 0; i < nc; i++) cin >> c[i]; for (int i = 0; i < nd; i++) cin >> d[i];
cout << min_time(a, na) + min_time(b, nb) + min_time(c, nc) + min_time(d, nd) << endl;
return 0; }
|
注意:以上代码为错误代码!!!!!!!!!
因为可以找到一组hack数据
按照贪心算法
10 6 5
10 6 3
max为21
但是实际上:
10 10
6 6 5 3
max为20
于是验证了贪心的不可行性,而这种退一步达到全局最优解的情况,就应该使用dp
这道题使用的是最基本的背包dp。我们由以上思路可以知晓,当两个脑子处理的内容大小最接近 的时候将会达到本组最优解。于是我给第一个脑子一个容量预算为sum/2(即背包容量),在运算过程中不能超过这个预算。通过将每个选择装进背包的物品的v等价于其重量w这种方法,由此得出最大化利用sum/2的背包空间所能装得下的处理总量,即dp[ sum/2 ];
而max(sum-dp[ sum/2 ],dp[ sum/2 ])即为某一组耗时。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| int dynamic_programming(int sum,int homework[],int n) { if(n==1)return homework[1]; int dp[2501]={0}; for(int i=1;i<=n;i++) { for(int j=sum/2;j>=homework[i];j--) { dp[j]=max(dp[j],dp[j-homework[i]]+homework[i]); } } return max(sum - dp[sum / 2], dp[sum / 2]); }
|
此外由于int是除以2(在正数范围内)向下取整的特性,可以数学证明sum-dp[ sum/2 ]总是大于dp[ sum/2 ],所以注释行也还可以那样输出。
最后,完整代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| #include <bits/stdc++.h> using namespace std; int a[5]; int dynamic_programming(int sum,int homework[],int n) { if(n==1)return homework[1]; int dp[2501]={0}; for(int i=1;i<=n;i++) { for(int j=sum/2;j>=homework[i];j--) { dp[j]=max(dp[j],dp[j-homework[i]]+homework[i]); } } return max(sum - dp[sum / 2], dp[sum / 2]); } int main() { int b[21] = {0},ans=0; for (int i = 1; i <= 4; i++) cin >> a[i]; for(int i=1;i<=4;i++) { int sum=0; memset(b,0,sizeof(b)); for(int j=1;j<=a[i];j++) { cin>>b[j]; sum+=b[j]; } ans+=dynamic_programming(sum,b,a[i]); } cout<<ans<<endl; return 0; }
|