题解:洛谷 P2392 kkksc03考前临时抱佛脚

题目https://www.luogu.com.cn/problem/P2392

解法1:DFS

对于每一科,我们需要将一些题目放入集合 A,其余的放入集合 B

我们设 A 集合中的元素个数为 xB 集合中的元素个数为 y,则答案为(ans):

ans \gets \min(ans,\max(\sum^x_{i=1}A_i,\sum^y_{i=1}B_i))

用 DFS 实现即可。

实现

#include<bits/stdc++.h>
using namespace std;
#define int long long
int s,a[25],S[5],ans,sum=1e18;
void dfs(int x,int A,int B){
	if(x>s){
		sum=min(sum,max(A,B));
		return;
	}
	dfs(x+1,A+a[x],B);
	dfs(x+1,A,B+a[x]);
}
signed main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	cin>>S[1]>>S[2]>>S[3]>>S[4];
	for(int i=1;i<=4;i++){
		s=S[i];
		for(int j=1;j<=s;j++){
			cin>>a[j];
		}
		sum=1e18;
		dfs(1,0,0);
		ans+=sum;
	}
	cout<<ans;
	return 0;
}

解法2:动态规划

可以先回顾一下前面的 DFS 写法。

在 DFS 的写法中,我们是将习题册的题目分成集合 A 和集合 B,使得 \sum^{|A|}_{i=1} A_i\sum^{|B|}_{i=1} B_i 的差最小。

也就是 \sum^{|A|}_{i=1} A_i 要尽可能地接近 \dfrac{m}{2}

那么 \dfrac{m}{2} 就是背包的容量,每道题的做题时间为物品的价格和价值。

对于第 k 本习题册:

dp_{i,j} 表示前 i 个物品选择一些放入剩余容量为 j 的背包中能获得的最大价值。

答案为:\sum^4_{i=1}\max(m-dp_{S_i,m\div 2},dp_{S_i,m\div 2})

其中 S_i 表示第 i 本习题册的题目数量,m 表示习题册题目用时之和。

a_i 表示第 i 道题目需要的时间。

状态转移:

dp_{i,j}= \begin{cases} \max(dp_{i-1,j},dp_{i-1,j-a_i}+a_i)& j\ge a_i \\ \ dp_{i-1,j-1} & \text{otherwise} \end{cases}

记得清空 dp 数组。

实现

/*
对于第 k 科: 

设 dp[i][j] 表示前 i 个物品选择一些放入剩余容量为 j 的背包中能获得的最大价值

答案:max(sum-dp[s[i]][m/2],dp[s[i]][m/2])(m 为习题题目完成时间之和)

状态转移: 

如果第 i 个物品能放入背包中(j>=a[i])则:dp[i][j]=max(dp[i-1][j],dp[i-1][j-a[i]]+a[i])

a[i] 表示第 i 道习题需要完成多久

否则:dp[i][j]=dp[i-1][j]

初始化:dp[i][j]=0
*/
#include<bits/stdc++.h>
using namespace std;
int s[5],a[25],dp[25][1205],ans;
void code(int n,int m){
	fill(dp[0],dp[n+1],0);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			dp[i][j]=dp[i-1][j];
			if(j>=a[i]){
				dp[i][j]=max(dp[i][j],dp[i-1][j-a[i]]+a[i]);
			}
		}
	}
	return;
}
signed main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	cin>>s[1]>>s[2]>>s[3]>>s[4];
	for(int i=1;i<=4;i++){
		int sum=0;
		for(int j=1;j<=s[i];j++){
			cin>>a[j];
			sum+=a[j];
		}
		code(s[i],sum/2);
		ans+=max(sum-dp[s[i]][sum/2],dp[s[i]][sum/2]);
	}
	cout<<ans;
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值