目录
时间限制: 1000ms
空间限制: 65536kb
? 艾尔迪亚人在与巨人的战斗中雷枪发挥了不可磨灭的作用,雷枪之所以能够发挥那么强的作用,是因为雷枪能够牢固的刺入巨人的身体内引爆。雷枪的核心威力来源自火药,而火药的原材料由硫磺、硝石与炭原材料制造而成的,而火药的原材料可以由矿洞产出。
?小埋是一个艾尔迪亚矿工,挖矿对于小埋来说能获得财富和荣誉(因为要抗击巨人需要大量材料,艾尔迪亚高层要鼓励矿工们多采矿),荣誉值到达一定值可以成为荣誉艾尔迪亚人。
? 现在小埋挖矿既想获得一定的财富也想获得一定量的荣誉。假设我们已知小埋挖的那部分矿洞有??块矿石,并且已知这??块矿石的重量?,对于每块矿石我们能够获得一些财富值和荣誉值,对于每块矿石的财富值和荣誉值设定如下。
小埋想要在自己需要至少获得财富值为??荣誉值为?的情况下,所挖出矿石总和的最小重量。
? 第一行输入一个整数??,表示矿石的数量。
? 第二行??个整数??分别表示每个矿石的重量。
? 第三行输入?和??表示我们需要至少获得的财富值和荣誉值。
输出占一行,输出一个整数,表示?输出小埋能够在限定条件下,挖出矿石总和的最小重量。
5
1 2 2 3 5
5 5
8
5
1 2 3 2 1
3 4
3
对于所有测试数据有:。
很明显,是最长上升子序列问题+二维费用背包问题。
把第??块到第??块矿石中以第???块矿石为起点的(非严格)递减序列长度最大值。(第?块矿石为起点矿石,就是必须选第?块矿石为起点)这句话就理解成第i块矿石为结尾,第n块矿石为开头的最长上升子序列,接着后面就是二维费用背包。
但是最长上升子序列朴素版是,这里会超时,所以得用二分优化成
#include<bits/stdc++.h>
using namespace std;
long long tmp[100010],f[100010],f1[100010],a[100010];
long long n;
long long m,v;
long long dp[1010][1010];
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
cin>>m>>v;
long long pos=1;
tmp[1] = a[1];
f[1] = 1;
for(int i=2;i<=n;i++)
{
long long l=1,r=pos;
while(l<r)
{
long long mid = (l+r+1)/2;
if(tmp[mid]<=a[i])
{
l = mid;
}
else
{
r = mid-1;
}
}
if(tmp[l]>a[i])
{
tmp[l] = a[i];
f[i] = l;
}
else
{
if(l == pos)
{
tmp[++pos] = a[i];
f[i] = pos;
}
else
{
tmp[l+1] = a[i];
f[i] = l+1;
}
}
}
//此时,f[i]就代表已第i个为结尾,第一个为开头的最长上升子序列
pos=1;
tmp[1] = a[n];
f1[n] = 1;
for(int i=n-1;i>=1;i--)
{
long long l=1,r=pos;
while(l<r)
{
long long mid = (l+r+1)/2;
if(tmp[mid]<=a[i])
{
l = mid;
}
else
{
r = mid-1;
}
}
if(tmp[l]>a[i])
{
tmp[l] = a[i];
f1[i] = l;
}
else
{
if(l == pos)
{
tmp[++pos] = a[i];
f1[i] = pos;
}
else
{
tmp[l+1] = a[i];
f1[i] = l+1;
}
}
}
//同理
memset(dp,0x3f,sizeof(dp));//dp初始化
dp[0][0] = 0;
for(int i=1;i<=n;i++)
{
for(int j=m;j>=0;j--)
{
for(int k=v;k>=0;k--)
{
dp[j][k] = min(dp[j][k],dp[max(j-f[i],0LL)][max(k-f1[i],0LL)]+a[i]);//二维费用背包
}
}
}
cout<<dp[m][v];
return 0;
}