CF544C&51nod1086【限定个数的完全背包】

47 篇文章 0 订阅

题意非常地晦涩难懂。

有n个程序员总共必须要码m行代码,其中每个程序员平均每行会出现的bug有x[i]个,问在总bug不超过b个的情况下有多少种方案(不是按每行是谁码的来区分,是按最终每个人码了多少行来区分)?

dp(i,j,k):前i个程序员写了j行产生k个BUG的方案数。i省略。

#include <bits/stdc++.h>
#define ll long long
//#define mod 1000000007
using namespace std;
int x[505];
ll dp[505][505];
int main(){
	int n,m,b,mod;
	cin>>n>>m>>b>>mod;
	for(int i=1;i<=n;++i)
		cin>>x[i];
	dp[0][0]=1;
	for(int i=1;i<=n;++i){ //如果n和m的for循环位置换一换就变成是按每行是谁码的来区分
		for(int j=1;j<=m;++j){
			for(int k=b;k>=x[i];--k){
				dp[j][k]=(dp[j][k]+dp[j-1][k-x[i]])%mod;
			}
		}
	}
	ll s=0;
	for(int i=0;i<=b;++i)
		s=(s+dp[m][i])%mod;
	cout<<s<<endl;
	return 0;
}

再看看另一道题。

有N种物品,每种物品的数量为C1,C2......Cn。从中任选若干件放在容量为W的背包里,每种物品的体积为W1,W2......Wn(Wi为整数),与之相对应的价值为P1,P2......Pn(Pi为整数)。求背包能够容纳的最大价值。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll w[105],p[105],c[105];
ll dp[500005];
int main(){
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;++i){
		cin>>w[i]>>p[i]>>c[i];
	}
	for(int i=1;i<=n;++i){
		for(int j=0;j<c[i];++j){
			for(int k=m;k>=w[i];--k){
				dp[k]=max(dp[k],dp[k-w[i]]+p[i]);
			}
		}
	}
	cout<<dp[m]<<endl;
	return 0;
}

很可惜,这样写就要超时了。该加点什么黑科技吧。。。

解法挺奇妙的。我服。

把每种物品的个数以二进制枚举每一位选和不选,这样减少了很大的复杂度。

然而碰到一个二进制不是全1的个数怎么办?

刚才突然反应过来:假如不是全1,只要换成枚举小于等于它的全1二进制数,再单独处理零头。

#include<bits/stdc++.h>  
#define ll long long  
using namespace std;  
ll w[105],p[105],c[105],d[105]; //c是小于等于它的全1二进制数,d是零头 
ll dp[500005];  
int main(){  
    int n,m;  
    cin>>n>>m;  
    for(int i=1;i<=n;++i){  
        cin>>w[i]>>p[i]>>c[i]; 
		int u=0,s=0,p=c[i];
		while(p>0){
			if(p%2!=1)
				u=1;
			s++;
			p/=2;
		} 
		if(u==1)
			s--;
		if(u==1)
			d[i]=c[i]-((1<<s)-1);
		c[i]=s;
    }  
    for(int i=1;i<=n;++i){  
        for(int j=0;j<c[i];++j){  
            for(int k=m;k>=w[i]*(1<<j);--k){  
                dp[k]=max(dp[k],dp[k-w[i]*(1<<j)]+p[i]*(1<<j));
            }  
        }  
//      for(int j=1;j<=d[i];++j){
//			for(int k=m;k>=w[i];--k){
//				dp[k]=max(dp[k],dp[k-w[i]]+p[i]);
//			}
//		}
		for(int k=m;k>=w[i]*d[i];--k){ //为什么零头都算上也能对? 
			dp[k]=max(dp[k],dp[k-w[i]*d[i]]+p[i]*d[i]);
		}
    }  
    cout<<dp[m]<<endl;  
    return 0;  
}  


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值