荣誉艾尔迪亚人的题解

目录

原题描述:

题目背景

题目描述

输入格式

输出格式

样例

Input 1

Output 1

Input 2

Output 2

数据范围:

样例解释

主要思路:

代码code:


原题描述:

时间限制: 1000ms

空间限制: 65536kb

题目背景


​ 艾尔迪亚人在与巨人的战斗中雷枪发挥了不可磨灭的作用,雷枪之所以能够发挥那么强的作用,是因为雷枪能够牢固的刺入巨人的身体内引爆。雷枪的核心威力来源自火药,而火药的原材料由硫磺、硝石与炭原材料制造而成的,而火药的原材料可以由矿洞产出。

题目描述


​小埋是一个艾尔迪亚矿工,挖矿对于小埋来说能获得财富和荣誉(因为要抗击巨人需要大量材料,艾尔迪亚高层要鼓励矿工们多采矿),荣誉值到达一定值可以成为荣誉艾尔迪亚人

​ 现在小埋挖矿既想获得一定的财富也想获得一定量的荣誉。假设我们已知小埋挖的那部分矿洞有 n 块矿石,并且已知这 n 块矿石的重量 a_1,a_2,...,a_n,对于每块矿石我们能够获得一些财富值和荣誉值,对于每块矿石的财富值和荣誉值设定如下。

  • 对于第i块矿石,它的财富值为前 i块矿石中以第 i 块矿石结尾的(非严格)递增序列长度最大值。(第i 块矿石为结尾矿石,就是必须选第i 块矿石为结尾)。
  • 对于第 i 块矿石,它的荣誉值为第 i 块到第 n 块矿石中以第 i  块矿石为起点的(非严格)递减序列长度最大值。(第i 块矿石为起点矿石,就是必须选第i 块矿石为起点)。

小埋想要在自己需要至少获得财富值为 m 荣誉值为 v的情况下,所挖出矿石总和的最小重量。

输入格式

​ 第一行输入一个整数 n ,表示矿石的数量。

​ 第二行 n 个整数a_1,a_2,...,a_n​ 分别表示每个矿石的重量。

​ 第三行输入m 和 v 表示我们需要至少获得的财富值和荣誉值。

输出格式

输出占一行,输出一个整数,表示​输出小埋能够在限定条件下,挖出矿石总和的最小重量。

样例

Input 1
5
1 2 2 3 5
5 5
Output 1
8
Input 2
5
1 2 3 2 1
3 4 
Output 2
3

数据范围:

对于所有测试数据有:0 \le n \le 10^5,0 \le m*v \le 10^3,1 \le a_i \le 10^9

样例解释

  • 对于样例1,我们可以选择前四个矿石[1,2,2,3]
    • 以第一个矿石为结尾的(非严格)递增序列为 [1],财富值为 1,以第一个矿石为开头的非严格递减序列为1,荣誉值为 1
    • 以第二个矿石为结尾的非严格递增序列为 [1,2],财富值为2,以第二个矿石为开头的非严格递减子序列为[2,2] ,荣誉值为 2
    • 以第三个矿石为结尾的非严格递增序列为 [1,2,2],财富值为3,以第三个矿石为开头的非严格递减子序列为[2] ,荣誉值为1
    • 以第四个矿石为结尾的非严格递增序列为[1,2,2,3],财富值为4,以第四个矿石为开头的非严格递减子序列为3,荣誉值为 1
    • 所以我们的财富值为 1+2+3+4 = 10,荣誉值为1+2+1+1 = 5,满足我们所需的至少获得财富值为 5,荣誉值为 5,并且所挖去的矿石总重量最小。
  • 对于样例2,我们可以选第二个矿石和第五个矿石,第二个矿石重量为 2,财富值为 2荣誉值为3,第五个矿石重量矿石重量为 1,财富值为 2,荣誉值为1,我们此时满足至少财富值3,且荣誉值为4 的情况,并且此时总重量最小为 3,或者我们也可以选择第一块矿石和第二块矿石。

主要思路:

很明显,是最长上升子序列问题+二维费用背包问题。

把第 i 块到第 n 块矿石中以第 i  块矿石为起点的(非严格)递减序列长度最大值。(第i 块矿石为起点矿石,就是必须选第i 块矿石为起点)这句话就理解成第i块矿石为结尾,第n块矿石为开头的最长上升子序列,接着后面就是二维费用背包。

但是最长上升子序列朴素版是O(n^2),这里会超时,所以得用二分优化成O(n \log_2^n)

代码code:

#include
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(la[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(la[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<

你可能感兴趣的:(题解,c++,dp,最长上升子序列,二维费用背包)