描述
(为什么老师要放炉石的图
题目大意: 你有一个可重集合A,A中的元素是正整数,当一个元素变为0时从集合中删除。 你可以花费p使一个元素+1,花费q使一个元素−1。 你有一次机会花费r使得集合中所有元素−1,若有元素因此被删除,则重复这个操作(不需要额外的花费)。 你的目标是最小化将集合变为空集的花费。
输入格式
第一行一个整数n表示场面上有n个随从。
接下来一行n个数,第ii个数a[i]为第i个随从的生命值。
接下来一行三个数p,q,r分别表示每张香蕉,月火术和亵渎在当前游戏中的费用。
输出格式
一行一个整数ans,表示为了清空场面至少需要的费用。
样例1
样例输入
5
1 1 5 6 7
5 4 2
样例输出
31
这题的贪心是有bug的,因为他不一定是一个严格递增的时候再使用亵渎,你目前的决策会影响后面的决策。
但是一个结论是没错的,我们肯定会在最后使用亵渎
其次,一个数在原数中的排名肯定是和最后使用亵渎时一样的
这是可以反证证出来的
fi,j
f
i
,
j
表示前
i
i
个数,最后一个数高度是的方案数
从
fi−1,j,fi−1,j−1
f
i
−
1
,
j
,
f
i
−
1
,
j
−
1
转移一下就行了
我写的是
fi,j
f
i
,
j
能减少的花费,所以我取得是
max
m
a
x
别忘了最后和不用亵渎的情况取个
min
m
i
n
,可能不用亵渎会更优
#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = j;i <= k;++i)
#define repp(i,j,k) for(int i = j;i >= k;--i)
#define ll long long
int n , a[5010];
int p , q , r;
ll Max;
ll f[5010][5010] , sum;
int read()
{
int sum = 0;char c = getchar();bool flag = true;
while( c < '0' || c > '9' ) {if(c == '-') flag = false;c = getchar();}
while( c >= '0' && c <= '9' ) sum = sum * 10 + c - 48 , c = getchar();
if(flag) return sum;
else return -sum;
}
void init()
{
n = read();
rep(i,1,n) a[i] = read() , sum = sum + a[i];
p = read();q = read();r = read();
sort(a + 1,a + n + 1);
return;
}
void work()
{
memset(f,-10,sizeof(f));
f[0][0] = 0;
sum = sum * q;
rep(i,1,n)
rep(j,1,i)//i个人高度为j
{
int fff = 0;
if(a[i] < j) fff = 1;
f[i][j] = max(f[i-1][j-1],f[i-1][j]);
f[i][j] = f[i][j] - 1ll * fff * p * (j-a[i]);
f[i][j] += 1ll * q * min(j,a[i]);
}
Max = 0;
rep(i,1,n)
rep(j,1,n)
Max = max(Max,f[i][j]);
Max -= r;
printf("%lld\n",min(sum,sum-Max));
return;
}
int main()
{
init();
work();
return 0;
}