二进制优化nbu 2417 有趣的买家

废话就不多说了,开始。。。

    标题链接:http://acm.nbu.edu.cn/v1.0/Problems/Problem.php?pid=2417

    

    标题粗心:

    已知有n个商品,第i个商品有Xi个,每一个商品Yi元(0<i<=n)。

    讯问m次,第j次讯问表示是不是存在若干个商品的代价和为Kj。

    0<n<=15,0<m<=100000

    0<Xi<=1000,0<Yi<=1000

    0<Kj<1000000

    时间限制是10s

    

    标题思绪:

    很容易可以判断出这是一道多重背包的标题,那么多重背包会超时吗?

    我们来算算看。

    多重背包的比拟容易的写法,是转化为01背包,那么对于该题转化为01背包的复杂度为O( max(Kj) * sum(Xi) )

    Kj最大为10^6,sum(Xi)最大为15*10^3,明显10s也是不够用的。

    那么优化一下,采取二进制优化,复杂度为O( max(Kj) * sum( log(Xi) ) )

    sum( log(Xi) )最大约为150(小于150),10s可解。

    那么何为二进制优化呢?

    首先要知道两种解多重背包的方法都是对商品进行拆分,

    01方法就是拆成每种商品个1个,

    而二进制则不然,而是拆成1、2、4、8、……,

    每日一道理
一个安静的夜晚,我独自一人,有些空虚,有些凄凉。坐在星空下,抬头仰望美丽天空,感觉真实却由虚幻,闪闪烁烁,似乎看来还有些跳动。美的一切总在瞬间,如同“海市蜃楼”般,也只是刹那间的一闪而过,当天空变得明亮,而这星星也早已一同退去……

    假如有某类商品有18件,那么就拆成1、2、4、8、3,

    为什么呢?

    因为1、2、4、8、3任意组合进行加法可以得到1~18,

    所以我们这样拆分该商品那么某商品被选择的全部可能条件都会出现。

    

    

    代码:

    

#pragma comment(linker, "/STACK:102400000,102400000")
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#include<ctype.h>
#include<iostream>
#include<algorithm>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<string>
using namespace std;
#define ll long long
#define clr(x,c,n) memset(x,c,sizeof(x[0])*(n))
#define clr_all(x,c) memset(x,c,sizeof(x))
#define IT iterator
#define ls rt<<1
#define rs ls|1
#define lson l,mid,ls
#define rson mid+1,r,rs
#define middle l+r>>1
#define MOD 1000000007
#define inf 0x3f3f3f3f
#define eps (1e-8)
#define PI 3.1415926535897932384626433832795
#define E 2.7182818284590452353602874713527
template <class T> T _min(T a,T b){return a<b? a:b;}
template <class T> T _max(T a,T b){return a>b? a:b;}
template <class T> T _abs(T a){return a>0? a:-a;}
template <class T> T _mod(T a,T m){return a<m? (a<0? (a%m+m)%m:a):a%m;}
template <class T> T _gcd(T a,T b){while(b){T t=b;b=a%b;a=t;}return a;}
template <class T> void _swap(T &a,T &b){T t=b;b=a;a=t;}
template <class T> void getmax(T &a,T b){a= a>b? a:b;}
template <class T> void getmin(T &a,T b){a= (a!=-1 && a<b)? a:b;}
int TS,cas=1;
const int M=100000+5;
int n,m;
int k[M],x[22],y[22],o;
bool dp[M*10];

void bag01(int val){
	for(int i=o;i>=val;i--) if(dp[i-val]) dp[i]=1;
}

void bagAll(int val){
	for(int i=val;i<=o;i++) if(dp[i-val]) dp[i]=1;
}

void bagMult(int p){
	if(x[p]*y[p] >= o) bagAll(y[p]);
	for(int i=1;i<x[p];i<<=1) bag01(i*y[p]),x[p]-=i;
	if(x[p]) bag01(x[p]*y[p]);
}

void run(){
	int i,j;
	for(i=1;i<=n;i++) scanf("%d%d",&x[i],&y[i]);
	o=0;
	for(i=1;i<=m;i++){
		scanf("%d",&k[i]);
		o=_max(o,k[i]);
	}
	for(i=1;i<=o;i++) dp[i]=0;
	dp[0]=1;
	for(i=1;i<=n;i++) bagMult(i);
	//for(i=1;i<=o;i++) printf("dp[%d] = %s\n",i,dp[i]? "true":"false");
	for(i=1;i<=m;i++) printf("%s\n", dp[k[i]]? "YES":"NO");
}

void presof(){
}

int main(){
	//freopen("input.txt","r",stdin);
    //freopen("output.txt","w",stdout);
	presof();
	//run();
	while(~scanf("%d%d",&n,&m)) run();
	//for(scanf("%d",&TS),cas=1;cas<=TS;cas++) run();
	return 0;
}

文章结束给大家分享下程序员的一些笑话语录: 刹车失灵
有一个物理学家,工程师和一个程序员驾驶着一辆汽车行驶在阿尔卑斯山脉 上,在下山的时候,忽然,汽车的刹车失灵了,汽车无法控制地向下冲去, 眼看前面就是一个悬崖峭壁,但是很幸运的是在这个悬崖的前面有一些小树 让他们的汽车停了下来, 而没有掉下山去。 三个惊魂未定地从车里爬了出来。
物理学家说, “我觉得我们应该建立一个模型来模拟在下山过程中刹车片在高 温情况下失灵的情形”。
工程师说, “我在车的后备厢来有个扳手, 要不我们把车拆开看看到底是什么 原因”。
程序员说,“为什么我们不找个相同的车再来一次以重现这个问题呢?”

--------------------------------- 原创文章 By 二进制和优化 ---------------------------------

原文地址:https://www.cnblogs.com/jiangu66/p/3095543.html