二重限制的01背包 最优解及其最优解的方案数

http://49.235.120.161/problem/10031

题意:N个物品  选每个物品i  将用时si+di   花费vi元   获得价值为1

问N个物品 总时间为T  钱包有M元  获得的最大价值Ans与Ans的方案数

N<=1000 T,M<=100

正着做: 

ll dp[1005][105][105]; 前i个物品 花时间至多j,花钱至多k的最大价值
ll dp2[1005][105][105];前i个物品 花时间至多j,花钱至多k的最大价值的方案数

注意dp2计算方案数是要初始化的

dp转移就是01背包嘛,就是选/不选的状态都枚举,哪个大选哪个

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod=1e9+7;
int N,T,M;
ll si[1005];
ll di[1005];
ll vi[1005]; 
ll dp[1005][105][105];//i 
ll dp2[1005][105][105];//

int main(){
	cin>>N>>T>>M;
	for(int i=1;i<=N;i++)cin>>si[i]>>di[i]>>vi[i];
	memset(dp,0,sizeof(dp));
	memset(dp2,0,sizeof(dp2));
	for(int j=0;j<=M;j++){
		for(int k=0;k<=T;k++){
			dp2[0][j][k]=1;	
		}
	}
	for(int i=1;i<=N;i++){
		for(int j=0;j<=M;j++){
			for(int k=0;k<=T;k++){
				if(j>=vi[i]&&k>=si[i]+di[i]){				
					if(dp[i-1][j][k]<dp[i-1][j-vi[i]][k-si[i]-di[i]]+1){
						dp[i][j][k]=dp[i-1][j-vi[i]][k-si[i]-di[i]]+1;
						dp2[i][j][k]=dp2[i-1][j-vi[i]][k-si[i]-di[i]]%mod;
					}
					if(dp[i-1][j][k]==dp[i-1][j-vi[i]][k-si[i]-di[i]]+1){
						dp[i][j][k]=dp[i-1][j][k];
						dp2[i][j][k]=(dp2[i-1][j-vi[i]][k-si[i]-di[i]] +dp2[i-1][j][k])%mod;
					}
					
					if(dp[i-1][j][k]>dp[i-1][j-vi[i]][k-si[i]-di[i]]+1){
						dp[i][j][k]=dp[i-1][j][k];
						dp2[i][j][k]=dp2[i-1][j][k]%mod;
					}
				}
				else{
					dp[i][j][k]=dp[i-1][j][k];
					dp2[i][j][k]=dp2[i-1][j][k];
				}		
			}
		}
	} 
	ll ans1=dp[N][M][T],ans2=dp2[N][M][T];

	cout<<ans1<<" "<<ans2%mod<<endl;
}

反着做:(滚动)但dp2的定义不同了,为恰好使用(前面是至多),dp1不变

#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
const int MOD=1e9+7;
int n,m,t,s[N],d[N],v[N];
int dp1[110][110],dp2[110][110];

int main()
{
	freopen("4.in","r",stdin);
	//freopen("7.out","w",stdout);
	cin>>n>>t>>m;
	for (int i=1;i<=n;i++) scanf("%d%d%d",&s[i],&d[i],&v[i]);
	for (int i=1;i<=n;i++) s[i]+=d[i];
	
	memset(dp1,-0x3f,sizeof(dp1));
	memset(dp2,-0x3f,sizeof(dp2));
	dp1[0][0]=0; dp2[0][0]=1;
	for (int i=1;i<=n;i++)
		for (int j=t;j;j--)
			for (int k=m;k;k--) {
				if (j<s[i] || k<v[i]) continue;
				if (dp1[j-s[i]][k-v[i]]+1>dp1[j][k]) {
					dp1[j][k]=dp1[j-s[i]][k-v[i]]+1;
					dp2[j][k]=dp2[j-s[i]][k-v[i]];
				} else if (dp1[j-s[i]][k-v[i]]+1==dp1[j][k]) {
					dp2[j][k]+=dp2[j-s[i]][k-v[i]];
					dp2[j][k]%=MOD;
				}
			}
	int ans1=0,ans2=0;
	for (int j=0;j<=t;j++) for (int k=0;k<=m;k++)
		if (dp1[j][k]>ans1) ans1=dp1[j][k],ans2=dp2[j][k];
		else if (dp1[j][k]==ans1) ans2=(ans2+dp2[j][k])%MOD;
	cout<<ans1<<" "<<ans2<<" "<<dp1[t][m]<<" "<<dp2[t][m]<<endl;			  
	return 0; 
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值