icpc昆明区域赛 热身赛第三题(DP)

这篇文章讲述了如何使用动态规划解决昆明热身赛中的一个题目,即在n个路灯中,通过最少操作将k个雕像按大小排序,通过状态转移和优化减少时间复杂度,达到O(n^2)。关键步骤包括定义状态、状态转移方程和优化策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

昆明热身赛第三题(DP)

赛后AC最为难过。。。。。。。。。。

题意:n栈路灯,在其中k栈路灯下都有一个雕像,每个雕像都有一个大小s。路灯从1标记到n。要做的是将这k个雕像按照s从小到大放到这n个路灯的其中k个位置中即可。将第某个雕像从i位置移动到j位置耗时 s* |i - j| ,求最小总耗时多少可以将雕像排序完毕。

每一个路灯有两个输入量,p表示雕像初始放置的路灯位置,s表示大小。

思路:先将路灯按照大小递增排序,大小相同时按照原来先后顺序。

dp[i][j]:表示前 i 个雕像放到前 j 个位置时的总耗时,第 i 个雕像放到了第 j 个位置。

状态转移:dp[i][j] 表示第 i 个雕像已经放到了第 j 个位置,那么要求这个值,只要找到前面 i - 1 个雕像放到 前面 j - 1 个位置的最小值即可。所以有
dp[i][j]=min(dp[i−1][i−1],dp[i−1][i],dp[i−1][i+1],...,dp[i−1][j−1])+s[i]∗∣p[i]−j∣ dp[i][j] = min(dp[i - 1][i - 1], dp[i - 1][i], dp[i - 1][i + 1], ..., dp[i - 1][j - 1]) + s[i] * | p[i] - j |dp[i][j]=min(dp[i1][i1],dp[i1][i],dp[i1][i+1],...,dp[i1][j1])+s[i]p[i]j

优化:不过现在的时间复杂度是 O(n^3) 的,N = 5e3,会T,需要优化一下。
观察上面的式子,可以发现
dp[i][j−1]=min(dp[i−1][i−1],dp[i−1][i],dp[i−1][i+1],...,dp[i−1][j−2])+s[i]∗∣p[i]−j+1∣dp[i][j - 1] = min(dp[i - 1][i - 1], dp[i - 1][i], dp[i - 1][i + 1], ..., dp[i - 1][j - 2]) + s[i] * | p[i] - j + 1 |dp[i][j1]=min(dp[i1][i1],dp[i1][i],dp[i1][i+1],...,dp[i1][j2])+s[i]p[i]j+1
所以这里可以进行优化!!!
dp[i][j]=min(dp[i][j−1]−s[i]∗∣p[i]−j+1∣,dp[i−1][j−1])+s[i]∗∣p[i]−j∣dp[i][j] = min(dp[i][j - 1] - s[i] * |p[i] - j + 1|, dp[i - 1][j - 1]) +s[i] * | p[i] - j |dp[i][j]=min(dp[i][j1]s[i]p[i]j+1,dp[i1][j1])+s[i]p[i]j

然后就可以愉快地进行编程了

#include<bits/stdc++.h>
using namespace std;

#define int long long
const int N = 5e3 + 17;
const int INF = 2e18;

int dp[N][N];

struct node{
    int p, s;
}a[N];

int cmp(node a, node b){
    //return a.s < b.s;
    if(a.s == b.s) return a.p < b.p;
    else return a.s < b.s;
}

signed main(){
    int n, k;
    cin >> n >> k;
    for(int i = 1; i <= k; ++i){
        cin >> a[i].p >> a[i].s;
    }
    sort(a + 1, a + k + 1, cmp);
    //memset(dp, 0x3f, sizeof dp);
    for(int i = 0; i < N; ++i)
        for(int j = 0; j < N; ++j)
            dp[i][j] = INF;
    for(int i = 1; i <= n; ++i){
        dp[1][i] = a[1].s * abs(a[1].p - i);
    }
    for(int i = 2; i <= k; ++i){
        for(int j = i; j <= n; ++j){
            dp[i][j] = min(dp[i][j - 1] - a[i].s * abs(a[i].p - j + 1), dp[i - 1][j - 1]) + a[i].s * abs(a[i].p - j);
            //cout << i << ' ' << j << ' ' << dp[i][j] << endl;
        }
    }
    int ans = dp[k][n];
    for(int i = k; i <= n; ++i)
        ans = min(ans, dp[k][i]);
    cout << ans;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值