#P2056. ABCD
#P2056. ABCD
题目描述
有 4 个长度为 N 的数组 a,b,c,d。现在需要你选择 N 个数构成数组 e,数组 e 满足 \(a[i] ≤e[i]≤b[i]\) 以及
\(\sum_{i=1}^{n}e[i]*c[i]=0\)
并且使得
\(\sum_{i=1}^Ne[i]*d[i]\)
最大。
输入格式
输入文件共 N+1 行。
第 1 行包含 1 个正整数 N。
第 i+1 行包含 4 个整数 a[i],b[i],c[i],d[i]。
输出格式
输出共 1 行,包含 1 个整数,表示所给出公式的最大值。输入数据保证一定有解。
样例
输入数据 1
5
-1 1 2 5
-2 2 1 2
0 1 1 3
-2 -1 3 10
-2 2 3 9
输出数据 1
2
输入数据 1
10
1 10 1 7
-10 10 2 0
-10 10 2 2
-10 10 2 0
1 10 1 0
-10 10 2 0
10 10 2 0
1 10 1 0
-10 10 2 0
1 10 1 0
输出数据 1
90
数据规模与约定
对于 20%的数据,\(N≤10;−2≤a[i]<b[i]≤2\)
对于 60%的数据,\(N≤50;−20≤a[i]<b[i]≤20\)
对于 100%的数据,\(N≤200;−25≤a[i]<b[i]≤25,1≤c[i]≤20;0≤d[i]≤100000\)
Solution
这道题可以看作一道多重背包的题,每个物品可以选 \(a[i]\sim b[i]\) 件,重量为 \(c[i]\) ,价值为 \(d[i]\) ,最终要求总重量为 \(0\) 即可。
可以最简单的多重背包暴力枚举,常数小一点时间可以卡过去,如果担心常数,单调队列优化即可。
如果不对 \(a[i],b[i],c[i]\) 做特殊处理, \(f\) 的第二维会出现负数,导致 \(\text{RE}\) ,解决办法就是在更新答案的时候将第二维加上 \(MAXN\) (此题 \(MAXN=a_{min}*N=50000\)),就可以使第二维全部为自然数而非负数,注意 \(f\) 的大小应该开到 \(MAXN \times 2\),因为第二维的大小在加上 \(MAXN\) 前为 \([-MAXN,MAXN]\) 。
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<limits.h>
#include<cmath>
#define mem(a,b) memset(a,b,sizeof(a));
using namespace std;
template<typename T> void read(T &k)
{
k=0;
T flag=1;char b=getchar();
while (b<'0' || b>'9') {flag=(b=='-')?-1:1;b=getchar();}
while (b>='0' && b<='9') {k=(k<<3)+(k<<1)+(b^48);b=getchar();}
k*=flag;
}
const int _SIZE=2e2;
int n;
int a[_SIZE+5],b[_SIZE+5],c[_SIZE+5],d[_SIZE+5];
const int MAXN=5e4;
int f[_SIZE+5][(MAXN<<1)+5];
int main()
{
read(n);
for (int i=0;i<=n;i++)
for (int j=0;j<=(MAXN<<1);j++)
f[i][j]=-INT_MAX/3;
for (int i=1;i<=n;i++)
{
read(a[i]);read(b[i]);
read(c[i]);read(d[i]);
}
int l=0,r=(MAXN<<1);
f[0][MAXN]=0;
for (int i=1;i<=n;i++)
{
for (int j=l;j<=r;j++)
{
for (int k=a[i];k<=b[i];k++)
{
if (j-k*c[i]>=l && j-k*c[i]<=r)
f[i][j]=max(f[i][j],f[i-1][j-k*c[i]]+k*d[i]);
}
}
}
//for (int j=MAXN-5;j<=MAXN+5;j++) printf("%d rm=%d\n",f[1][j],j-MAXN);puts("");
printf("%d\n",f[n][MAXN]);
return 0;
}