题意:
你要按顺序买 n n 个物品,每个物品的花费分别为。初始时有一张价值为 b b 的代金券。
每次最多可以使用元的代金券,此时如果消费x元,则能得到 ⌊x/10⌋ ⌊ x / 10 ⌋ 元新的代金券。
问需要花费的最少代价。
n<=5000,ai<=1000,∑ai<=105,b<=105 n <= 5000 , a i <= 1000 , ∑ a i <= 10 5 , b <= 10 5
做法:
感觉这题dp的做法还是挺好理解的吧。
f[i][j] f [ i ] [ j ] 表示前i天得到了j元的代金券所用的最少钱数。
记 sum[i] s u m [ i ] 表示 a[i] a [ i ] 的前缀和,则第i天能用的代金券钱数就等于 b−(sum[i]−f[i][j])+j b − ( s u m [ i ] − f [ i ] [ j ] ) + j 。
转移的时候直接枚举花多少代金券即可,然后由于要输出方案就记录一个 pre p r e 数组保存最优方案。
然后这样复杂度看起来有点大qwq。
考虑 i 和 k 的循环时间复杂度为 ∑ai2≤5×104 ∑ a i 2 ≤ 5 × 10 4 , j≤∑ai10≤104 j ≤ ∑ a i 10 ≤ 10 4 ,因此整个时间复杂度是 O((∑ai)220) O ( ( ∑ a i ) 2 20 ) ,最坏情况计算次数 ≤5×108 ≤ 5 × 10 8 。由于时限 3s ,因此是可以过的(事实上最慢的点跑了还不到 1shhhh)。
——摘自这里
然而我发现我的程序总共只跑了200+ms。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<iostream>
#include<cmath>
#include<cstdlib>
#include<cctype>
using namespace std;
typedef long long ll;
inline ll read() {
char ch = getchar(); ll x = 0; int op = 1;
for(; !isdigit(ch); ch = getchar()) if(ch == '-') op = -1;
for(; isdigit(ch); ch = getchar()) x = x*10+ch-'0';
return x*op;
}
inline void write(ll a) {
if(a < 0) putchar('-'), a = -a;
if(a >= 10) write(a/10); putchar('0'+a%10);
}
const int N = 5005, M = 10005;
const int inf = 1e9;
int n, m, now;
int a[N], sum[N], f[M], g[M], b[N], pre[N][M];
int main() {
n = read(); m = read();
for(int i = 1; i <= n; i ++) sum[i] = sum[i-1]+(a[i] = read());
//f[i][j]表示前i天得到了j元的代金券所用的最少钱数,加滚动数组
memset(f, 0x3f, sizeof f);
f[0] = 0;
for(int i = 1; i <= n; i ++) {
now = min(now+a[i]/10, M);
for(int j = 0; j <= now; j ++) { g[j] = f[j]; f[j] = inf; }//滚动
for(int j = 0; j <= now; j ++) if(g[j] <= sum[i-1]) {//枚举1~i-1天得到了多少代金券
int t = m-(sum[i-1]-g[j])+j;//sum[i-1]-g[j]就是1~i-1天用的代金券钱数
//t就是目前剩下的代金券钱数
for(int k = 0; k <= a[i]/2 && k <= t; k ++) {//枚举第i天用多少代金券
int r = (a[i]-k)/10;//能获得这么多新的代金券
if(g[j]+a[i]-k < f[j+r]) {
f[j+r] = g[j]+a[i]-k;
pre[i][j+r] = k;//记录最优方案
}
}
}
}
int ans = 0;
for(int i = 1; i <= now; i ++) if(f[i] < f[ans]) ans = i;
write(f[ans]); puts("");
for(int i = n; i >= 1; i --) {
b[i] = pre[i][ans];
ans -= (a[i]-pre[i][ans])/10;
}
for(int i = 1; i <= n; i ++) write(b[i]), putchar(' ');
return 0;
}