JZOJ 100044. abcd

版权声明:喜欢请点个大拇指,感谢各位dalao。弱弱说下,转载要出处呦 https://blog.csdn.net/qq_35786326/article/details/85015143

目录大法好


题目:

传送门


分析:

比较容易看出来这是一道多重背包的题.但是朴素做法肯定过不了,需要优化.比较常见的优化是二进制法和单调队列.这道题用二进制法比较好处理
由于 [ a i , b i ] [a_i,b_i] 中可能包含负数,所以需要对式子变形:
[ a i , b i ] > [ 0 , b i a i ] + a i = e i [a_i,b_i] -> [0,b_i-a_i] + a_i = e_i
e i c i = 0 \sum e_i*c_i = 0 ,因为要让背包的容积是最大的,所以 e i e_i b i a i b_i-a_i ,那么 e i c i = 0 \sum e_i*c_i = 0 > -> ( b i a i ) c i + a i c i = 0 \sum (b_i - a_i)*c_i + a_i*c_i = 0
移项,可以得到背包的容积为 a i c i -\sum a_i*c_i
要最大化 e i d i > ( b i a i ) d i + a i d i \sum e_i*d_i -> \sum(b_i-a_i)*d_i + a_i*d_i
这样变形后, e i e_i 的取值范围就变成了 [ 0 , b i a i ] [0,bi-ai] ,就可以分解成二进制数来做了


代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring> 
#include<cstdlib>
#include<algorithm>
#include<set>
#include<queue>
#include<vector>
#include<map>
#include<list>
#include<ctime>
#include<iomanip>
#include<string>
#include<bitset>
#include<deque>
#include<set>
#define LL long long
#define ch cheap
using namespace std;
inline LL read() {
    LL d=0,f=1;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){d=d*10+s-'0';s=getchar();}
    return d*f;
}
int max(int x,int y) {return x>y? x:y;}
int a[205],b[205],c[205],d[205];
int cnt=0,w[200005],v[200005],f[200005];
void yh(int num,int x,int y)
{
	for(int i=1;i<=num;i*=2)
	{
		v[++cnt]=x*i;
		w[cnt]=y*i;
		num-=i;
	}
	if(num)
	{
		v[++cnt]=x*num;
		w[cnt]=y*num;
	}
	return;
}
int main()
{
	int n=read(),ans=0,m=0;
	for(int i=1;i<=n;i++) 
	{
		a[i]=read(),b[i]=read(),c[i]=read(),d[i]=read();
		b[i]-=a[i];
		ans+=a[i]*d[i];
		m-=c[i]*a[i];
	}
	for(int i=1;i<=n;i++)
	  yh(b[i],c[i],d[i]);
	memset(f,-127/3,sizeof(f));
	f[0]=0;
	for(int i=1;i<=cnt;i++)
	  for(int j=m;j>=v[i];j--)
	    f[j]=max(f[j],f[j-v[i]]+w[i]);
	cout<<ans+f[m];
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_35786326/article/details/85015143
今日推荐