AtCoder ABC 060 D - Simple Knapsack

乍一看是01背包问题,wocao,太简单,这个我做过好多次,绝对没问题!第一次提交CE???What?怎么可能呢?好吧,数组开太大了,w在十亿内,数组开不了,那我数组开小一点吧,一亿,What?RE?这也不行,那也不行,看来这道题没这么简单。
  又倒回去仔细读了一下题目,题目本身就是简单背包,一共才三句话,不可能理解错误,唯一的不同就是这道题的约束条件范围非常的大,然后突然发现:

For each i=2,3,...,N, w1≤wi≤w1+3.

这个条件在一般的背包问题里面好像没有,什么意思呢?对于2~i的每一个重量,它都在w1~w1+3这个重量范围之间,换句话说就是所有的w1只有四种可能,w1、w1+1、w1+2、w1+3,这个对于解题有什么作用呢?

  1. 我们可以将所有的物品按重量将价值分为4类,每一类的数量最多不超过100种(总共N才最大100)
  2. 将每一类按价值从大到小排序(后面就从价值大的开始装)
  3. 每一类可以装1~cnti个(cnti是第i类的物品数量),装1个价值就是vi[1],装2个价值就是vi[1]+vi[2],装cnti个价值就是vi[1]+vi[2]+```+vi[cnti],也就是用循环把所有价值叠加起来
  4. 将每一类的可以选取的数量进行枚举,只要总重量小于w,并且总价值比当前总价值大,那么久更新总价值
  5. 总重量怎么算?我们是按重量进行分类的,基准是w1,那么每一类的重量你是知道的,假设第一类选取了i个,那么总重量就是w1*i,第二类选取了j个,那么总重量就是(w1+1)*j,以此类推,四类的重量都可以算
  6. 这道题要设4类数组存放价值,统计四类的数量,注意不要把各类变量写错了(什么v1,v2,v3,v4,cnt1,cnt2,cnt3,cnt4,真的巨容易弄混(T▽T))
      下面是我的代码:
#include<cstdio>

#include<cstring>
#include<algorithm>
#define ll long long
#define MAXN 100000005

using namespace std;
//喜欢用结构体来存放每个物体的价值和重量,不太符合常规,哈哈哈 
//等同于w[105],v[105] 
struct TH{
	ll w,v;
}th[105];
//按重量分四类用于存放价值的数组 
ll v1[105],v2[105],v3[105],v4[105];
//每一类的数量 
int cnt1=0,cnt2=0,cnt3=0,cnt4=0; 
//后面要从大到小排序 
bool cmp(ll a,ll b){
	return a>b;
}

int main(){
	ll n,w;
	scanf("%lld %lld",&n,&w);
	for(int i=1;i<=n;i++){
		scanf("%lld %lld",&th[i].w,&th[i].v);
		//第一个直接存到v1数组,从数组下标为1的地方开始存 
		if(i==1){
			v1[++cnt1]=th[i].v;
			continue;
		}
		//1开始分类咯,以w[1]为基准, 按重量把所有价值分为4类 
		int con=th[i].w-th[1].w;
		switch(con){
			case 0:v1[++cnt1]=th[i].v;break;
			case 1:v2[++cnt2]=th[i].v;break;
			case 2:v3[++cnt3]=th[i].v;break;
			case 3:v4[++cnt4]=th[i].v;break;
		}
	}
	//2每一类按价值价值从大到小排序 
	sort(v1+1,v1+cnt1+1,cmp);
	sort(v2+1,v2+cnt2+1,cmp);
	sort(v3+1,v3+cnt3+1,cmp);
	sort(v4+1,v4+cnt4+1,cmp);
	//3每种类型的物品价值叠加 
	for(int i=2;i<=cnt1;i++){
		v1[i]+=v1[i-1];
	} 
	for(int i=2;i<=cnt2;i++){
		v2[i]+=v2[i-1];
	} 
	for(int i=2;i<=cnt3;i++){
		v3[i]+=v3[i-1];
	} 
	for(int i=2;i<=cnt4;i++){
		v4[i]+=v4[i-1];
	} 
	//4准备完毕,可以开始找答案了 
	ll ans=0;
	//每一类的枚举从0开始,v[0]肯定是0,因为我们的价值存放在1~cnt位嘛,所以不用担心影响结果 
	// 为什么必须从0开始?因为我可以选择这一类一个都不选啊 
	for(int i=0;i<=cnt1;i++){//枚举 
		for(int j=0;j<=cnt2;j++){
			for(int k=0;k<=cnt3;k++){
				for(int h=0;h<=cnt4;h++){
					//枚举完数量就可以计算重量咯 
					if(th[1].w*(i)+(th[1].w+1)*(j)+(th[1].w+2)*(k)+(th[1].w+3)*(h)<=w){
						//如果重量可以装下,就看总价值有没有更大咯 
						ans=max(ans,v1[i]+v2[j]+v3[k]+v4[h]);
					}
				}
			}
		}
	}
	printf("%lld",ans);
	return 0;
} 

找了一个别人的答案,看起来就要高级很多:

#include <bits/stdc++.h>
//---------------------------
using namespace std;
//---------------------------
#define REP(i,n) for(int i = 0; i < (n); i++)
 
long long N,W,sm=0;
long long w[200];
long long v[200];
vector<long long> v1,v2,v3,v4;
int i,j,k,l,con;
int cnt[4];
 
int main(){
  std::ios::sync_with_stdio(false);
  std::cin.tie(0);
 
  cin >> N >> W;
 
  REP(i,N){
    cin >> w[i] >> v[i];
    if(i == 0){
      v1.push_back(v[i]);
    }
    if(i>=1){
      con=w[i]-w[0];
      if(con==0)v1.push_back(v[i]);
      if(con==1)v2.push_back(v[i]);
      if(con==2)v3.push_back(v[i]);
      if(con==3)v4.push_back(v[i]);
    }
  }
  sort(v1.begin(), v1.end());
  sort(v2.begin(), v2.end());
  sort(v3.begin(), v3.end());
  sort(v4.begin(), v4.end());
 
  reverse(v1.begin(), v1.end());
  reverse(v2.begin(), v2.end());
  reverse(v3.begin(), v3.end());
  reverse(v4.begin(), v4.end());
 
  for(i=0;i<(int)v1.size();i++){
    if(i+1==(int)v1.size())continue;
    v1[i+1]+=v1[i];
  }
  for(i=0;i<(int)v2.size();i++){
    if(i+1==(int)v2.size())continue;
    v2[i+1]+=v2[i];
  }
  for(i=0;i<(int)v3.size();i++){
    if(i+1==(int)v3.size())continue;
    v3[i+1]+=v3[i];
  }
  for(i=0;i<(int)v4.size();i++){
    if(i+1==(int)v4.size())continue;
    v4[i+1]+=v4[i];
  }
  v1.insert(v1.begin(), 0);
  v2.insert(v2.begin(), 0);
  v3.insert(v3.begin(), 0);
  v4.insert(v4.begin(), 0);
 
  REP(i, (int)v1.size()){
    REP(j, (int)v2.size()){
      REP(k, (int)v3.size()){
        REP(l, (int)v4.size()){
          if(i*w[0]+j*(w[0]+1)+k*(w[0]+2)+l*(w[0]+3) <= W){
            sm = max(sm, v1[i]+v2[j]+v3[k]+v4[l]);
          }
        }
      }
    }
  }
  cout << sm << endl;
  return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值