Kirill the Gardener 3 URAL - 2072 DP

Kirill the Gardener 3 URAL - 2072 DP

题目链接
题目大意:有n朵花,要求按照权值递增的顺序浇水,浇水一次花一个时间,走一个单位长度花费一个单位时间。问最小花费。
input:
6
3 2 5 6 2 5
output: 21
比赛的时候没有想出是什么思路,后来听别人说是dp,就又想了想,然而还是没有想出来。想到了这个题的关键点是有相同点的时候应该怎么选择。
正解:首先要明白一点,就是当有一堆相同的值得时候,我们只需要计算最左边的和最右边的,因为最优解只有可能从最左边走到最右边或者从最右边走到最左边,就是走直线,对于不相同的直接走就ok。现在问题是,相同的是走左边金凤还是走右边近,这就需要dp了。
先从小到大排个序,记录每个值最左边的位置和最右边的位置。
dp[i][0]表示跑第i个数从左走到右。
dp[i][1]表示跑第i个数从右走到左。

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

typedef long long ll;
const int maxn = 1e5 + 100;
const ll inf = 0x3f3f3f3f;


struct node {
   int data;
   ll pos1, pos2;
   bool operator <(const node &a) const {
       if(data != a.data) return data < a.data;
       return pos1 < a.pos1;
   }
}p[maxn], t[maxn];

ll dp[maxn][2];

int main()
{
    int n;
    scanf("%d", &n);
    for(int i = 0; i < n; i++)
    {
        scanf("%d", &p[i].data);
        p[i].pos1 = (ll)i;
    }

    sort(p, p + n);
    t[0] = (node) {0, 0, 0};
    int cnt = 1;
    t[cnt].data = p[0].data;
    t[cnt].pos1 = p[0].pos1;
    for(int i = 0; i < n; i++) {
        if(i == n - 1 || p[i].data != p[i + 1].data) {
            t[cnt++].pos2 = p[i].pos1;
        }
        if(i != n - 1 && p[i + 1].data != p[i].data) {
           t[cnt].data = p[i + 1].data;
           t[cnt].pos1 = p[i + 1].pos1;
        }
    }

    dp[0][0] = dp[0][1] = 0;
    for(int i = 1; i < cnt; i++) {
        dp[i][0] = min(dp[i - 1][0] + abs(t[i - 1].pos2 - t[i].pos1), dp[i - 1][1] + abs(t[i - 1].pos1 - t[i].pos1)) + abs(t[i].pos1 - t[i].pos2);
        dp[i][1] = min(dp[i - 1][0] + abs(t[i - 1].pos2 - t[i].pos2), dp[i - 1][1] + abs(t[i - 1].pos1 - t[i].pos2)) + abs(t[i].pos1 - t[i].pos2);
    }
    printf("%lld\n", min(dp[cnt - 1][0], dp[cnt - 1][1]) + n);
}

猜你喜欢

转载自blog.csdn.net/deerly_/article/details/80424025
今日推荐