n个物品中,必须购买m个,每次购买的费用为该物品的价格加上c[排名]。

对于一个物品,买他后面的物品不影响他的价格;若他前面有物品被买,它的排名可以靠前,也就是可以选择前面更低价格的c。

f[i][j]表示前i个物品,其中不超过i的必买的物品已买,共买了j个物品的最优值。
如果第i个物品非必买,那么f[i][j] = f[i-1][j],之前买了j个物品就行。
否则,该物品可买可不买,买的话,之前买了j-1个物品,当前费用为a[i]+c[...]。

暴力超时代码:

#include <bits/stdc++.h>
#define N 5005
using namespace std;
int n, m, i, j, k, v, a[N], c[N], x[N], u[N];
long long f[N][N], ans=1e18;
int main(){
    scanf("%d%d", &n, &m);
    for(i=1; i<=n; i++){
        scanf("%d", &a[i]);
    }
    for(i=1; i<=n; i++){
        scanf("%d", &c[i]);
    }
    for(i=1; i<=m; i++){
        scanf("%d", &x[i]);
        u[x[i]] = 1;
    }
    memset(f, 9, sizeof(f));
    for(i=0; i<x[1]; i++) f[i][0] = f[0][0] = 0;
    for(i=1; i<=x[m]; i++){
        for(j=1; j<=i; j++){
            if(u[i] == 0) f[i][j] = f[i-1][j];
            for(v=1e9, k=0; k<j; k++) v = min(v, c[i-k]);
            f[i][j] = min(f[i][j], f[i-1][j-1]+a[i]+v);
        }
    }//前i个选了j个搞定的最优值
    for(i=m; i<=n; i++) ans = min(ans, f[x[m]][i]);
    printf("%lld\n", ans) ;
    return 0;
}

前缀和优化代码:(预处理区间最小值也可以)

#include <bits/stdc++.h>
#define N 5005
using namespace std;
int n, m, i, j, k, v, a[N], c[N], x[N], u[N];
long long f[N][N], ans=1e18;
int main(){
    scanf("%d%d", &n, &m);
    for(i=1; i<=n; i++) scanf("%d", &a[i]);
    for(i=1; i<=n; i++) scanf("%d", &c[i]);
    for(i=1; i<=m; i++){
        scanf("%d", &x[i]);
        u[x[i]] = 1;
    }
    memset(f, 9, sizeof(f));
    for(i=0; i<x[1]; i++) f[i][0] = f[0][0] = 0;//x1开始至少选1个
    for(i=1; i<=x[m]; i++){
        for(v=1e9, j=1; j<=i; j++){
            if(u[i] == 0) f[i][j] = f[i-1][j];//非必选
            v = min(v, c[i-j+1]);//没多买一个,往左多一个选择
            f[i][j] = min(f[i][j], f[i-1][j-1]+a[i]+v);
        }
    }//前i个选了j个搞定的最优值
    for(i=m; i<=n; i++) ans = min(ans, f[x[m]][i]);//至少选m个
    printf("%lld\n", ans) ;
    return 0;
}

作者 crxis

发表回复