2021.6.8 背包模拟 总结

前言

一次简单的dp考试,但是由于审题错误 和 复杂度计算问题导致卡分……
详见下

1.打包

挺简单的一个01背包,就不详细写了。

2.暗黑破坏神

题面

思路

可以把每个药水转化成分组背包,但是重点就是对于无后效性的dp,如何记录前面的状态。
本来思路是先正常跑dp,将最大价值和最小花费求出,后用dfs跑满来找到这个方案。
但是dfs的复杂度计算错误,导致错误,只有45分……

后来也没有太好的解决办法,就改成每进行一次dp记录一下每个药水选了多少次吧……

代码



#include <bits/stdc++.h>
#define fo(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout)
using namespace std;
const int INF = 0x3f3f3f3f , N = 1e2+5 , M = 5e2+5;
typedef long long ll;
typedef unsigned long long ull;
#define int ll
inline ll read(){
	ll ret = 0 ; char ch = ' ' , c = getchar();
	while(!(c >= '0' && c <= '9')) ch = c , c = getchar();
	while(c >= '0' && c <= '9')ret = (ret << 1) + (ret << 3) + c - '0' , c = getchar();
	return ch == '-' ? - ret : ret;
}
int n,m,p[M],w[N];
int c[N][N],sum,ans[M][M],dp[M][M];
signed main(){
	n = read() , m = read();
	for(int i = 1 ; i <= n ; i ++){
		w[i] = read() , p[i] = read();
		for(int j = 1 ; j <= p[i] ; j ++)
			scanf("%lld",&c[i][j]);
	}
	for(int i = 1 ; i <= n ; i ++)
		for(int j = m ; j >= w[i] ; j --)
			for(int k = 1 ; k <= p[i] ; k ++){
				if(j < k * w[i]) continue;
					if(dp[j][0]<dp[j-k*w[i]][0]+c[i][k]){
						dp[j][0]=dp[j-k*w[i]][0]+c[i][k];
						dp[j][i]=k;
						for(int h = i - 1 ; h >= 1 ; h --)
							dp[j][h] = dp[j-k*w[i]][h];
					}
					if(dp[j - k * w[i]][0] + c[i][k] == dp[j][0]){
						int s1 = 0 , s2 = 0;
						for(int h = 1 ; h <= n ; h ++){
							s1 += w[h] * dp[j][h];
						}
						s2 += k * w[i];
						for(int h = i - 1 ; h >= 1 ; h --)
							s2+=w[h]*dp[j-k*w[i]][h];
						if(s2 < s1){
							dp[j][i] = k;
							for(int h = i - 1 ; h >= 1 ; h --)
								dp[j][h] = dp[j-k*w[i]][h];
					}
				}
			}
	for(int i=0;i<=n;i++)
		printf("%lld
",dp[m][i]);
	return 0;
}

3.科技庄园

题面

思路

刚看题觉得好麻烦……
后来发现有用的信息:

  • 总时间,总体力
  • 每棵树的桃数
  • 每棵树所耗的时间和体力
  • 每棵树能摘的次数

再仔细想一下,可以转化成多重背包。
只需要总时间和总体力取(min)就是背包的大小
把每个有果子的树取出来,就变成了重量为(2 imes(i+j))的多重背包。
注意的是:他不想摘很多的桃以至体力为0,说明体力需要-1!
卡分就是因为这个问题!
另外,地图给的范围是(100*100),所以要开背包大小为(100^2 *log(100)!)RE来源

#include <bits/stdc++.h>
#define fo(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout)
using namespace std;
const int INF = 0x3f3f3f3f , N = 1e2+5 , M = 1e4+5;
typedef long long ll;
typedef unsigned long long ull;
#define int ll
inline ll read(){
	ll ret = 0 ; char ch = ' ' , c = getchar();
	while(!(c >= '0' && c <= '9')) ch = c , c = getchar();
	while(c >= '0' && c <= '9')ret = (ret << 1) + (ret << 3) + c - '0' , c = getchar();
	return ch == '-' ? - ret : ret;
}
int n,m,V,w[M<<8],v[M<<8],dp[N],cnt;
signed a[N][N],b[N][N];
signed main(){
	int x,y;
	n = read() , m = read() , x = read() , y = read()-1;
	V = min(x,y);
	for(int i = 1 ; i <= n ; i ++)
		for(int j = 1 ; j <= m ; j ++)
			a[i][j] = read();
	for(int i = 1 ; i <= n ; i ++)
		for(int j = 1 ; j <= m ; j ++){
			x = read();
			for(int k = 1 ; k <= x ; k <<= 1)
				w[++cnt] = k * (i+j)*2 , v[cnt] = k * a[i][j] , x -= k;
			if(x)
				w[++cnt] = x * (i+j)*2 , v[cnt] = x * a[i][j];
		}
//	for(int i = 1 ; i <= cnt ; i ++)
//		printf("%lld:%lld %lld
",i,w[i],v[i]);
	for(int i = 1 ; i <= cnt ; i ++)
		for(int j = V ; j >= w[i] ; j --)
			dp[j] = max(dp[j],dp[j-w[i]]+v[i]);
	printf("%lld",dp[V]);
	return 0;
}

4.金明的预算方案

题面

思路

还是一道经典题啦……因为一次A了就不详细写了。
对于每个主件,最多有2个附件,因此在01背包的时候增加为:

  • 只选择当前主件
  • 选择当前主件和第1个附件
  • 选择当前主件和第2个附件
  • 选择当前主件和第1、2个附件

于是把dp的地方略微修改就可以了

#include <bits/stdc++.h>
#define fo(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;
const int INF = 0x3f3f3f3f,N = 3.2e4+5 , M = 1e2+5;
typedef long long ll;
typedef unsigned long long ull;
inline ll read(){
	ll ret = 0 ;char ch = ' ' , c = getchar();
	while(!(c >= '0' && c <= '9'))ch = c , c = getchar();
	while(c >= '0' && c <= '9')ret = (ret << 1) + (ret << 3) + c - '0' , c = getchar();
	return ch == '-' ? -ret : ret;
}
int n,m;
int w[M],v[M],ma[M][3],cnt,cntm,wa[M],va[M]; 
int dp[N],to[N];
signed main(){
	n = read() , m = read();
	int a,b,c;
	for(int i = 1 ; i <= m ; i ++){
		a = read() , b = read() , c = read();
		if(!c)
			to[i] = ++cnt,w[cnt] = a, v[cnt] = b * a;
		else
			 ma[to[c]][ ++ma[to[c]][0] ] = ++cntm , wa[cntm] = a , va[cntm] = b * a;
	}
	for(int i = 1 ; i <= cnt ; i ++){
		for(int j = n ; j >= w[i] ; j --){
			dp[j] = max(dp[j],dp[j-w[i]]+v[i]);
			if(j >= w[i] + wa[ma[i][1]])
				dp[j] = max(dp[j],dp[j-w[i]-wa[ma[i][1]]] + v[i] + va[ma[i][1]]);
			if(j >= w[i] + wa[ma[i][2]])
				dp[j] = max(dp[j],dp[j-w[i]-wa[ma[i][2]]] + v[i] + va[ma[i][2]]);
			if(j >= w[i] + wa[ma[i][1]] + wa[ma[i][2]])
				dp[j] = max(dp[j],dp[j-w[i]-wa[ma[i][1]]-wa[ma[i][2]]] + v[i] + va[ma[i][1]] + va[ma[i][2]]);
		}
	}
	int ans = -INF;
	for(int i = 0 ; i <= n ; i ++)
		ans = max(ans,dp[i]);
//		printf("%d:%d
",i,dp[i]);
	printf("%d",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/Shinomiya/p/14869349.html