POJ-3612 Telephone Wire 动态规划

这题的状态推倒极富想象力,原来写过的代码又忘了如何去写了......

题意:其实就和以前做过的一道我要长高一模一样,给定N个数字,现在要求连续的两个数字之差的绝对值乘以C最小,每个数字可以变大,变大所带来的开销为变量的平方.

  状态方程为 dp[i][j] = min(dp[i-1][k] + C*|j-k| + (j-H[i]) ^ 2)

  if (j >= k)   dp[i][j] = min(dp[i-1][k] -  C*k) +C*j + (j-H[i])^2
  if (j <= k)   dp[i][j] = min(dp[i-1][k] + C*k) - C*j + (j-H[i])^2

  状态的是通过 i-1 行来推导第 i 行的状态,这里的技巧在于

  for (int j  = H[i-1]; j <= 100; ++j)  Min = min(Min, dp[i-1][j] - C*k)

  这个循环来遍历 i-1 行的所有合法高度,配合下面那条语句,就能够得到满足当前的j>Min中的k
  也就是Min中的保留的最小值时的k一定是小于等于当前我们枚举到的j,也就满足了第一个式子,后面就只需要判定一下当前的j是否能够有H[i]得到,也就是j >= H[i]

  for (int j = 100; j >= H[i]; --j) Min = min(Min, dp[i-1][j] + C*k) 

  这里的边界条件不是H[i-1],因为我们要求的推导的第i行的状态数应该是从H[i] - 100因此要把边界控制成H[i]

代码如下:

#include <cstdlib>
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define INF 0x3f3f3f3f
#define MAXN 100000
using namespace std;

int N, C, seq[MAXN+5];
int dp[2][105]; // dp[i][j]表示从到第i个数字高度为j时的最小值

inline int pow2(int x) {
    return x * x;
}

int DP() {
    int Min;
    memset(dp, 0x3f, sizeof (dp));
    for (int j = seq[1]; j <= 100; ++j) { // 只能够增加 
        dp[1][j] = pow2(j-seq[1]);
    }
    for (int i = 2; i <= N; ++i) {
        int t = i&1;    
        Min = INF;
        for (int j = 1; j <= 100; ++j) dp[t][j] = INF; // 每次都需要初始化 
        for (int j = seq[i-1]; j <= 100; ++j) { // 枚举上一层的状态,来推倒当前层的状态
        // 从小到大枚举i-1层,则Min中保存的值对应的i-1层的高度一定小于j
            Min = min(Min, dp[!t][j]-C*j);
            if (j >= seq[i]) {
                dp[t][j] = min(dp[t][j], Min + C*j + pow2(j-seq[i]));
            }
        }
        Min = INF;
        for (int j = 100; j >= seq[i]; --j) { // 保证所有合法的状态都予以求值
            Min = min(Min, dp[!t][j]+C*j);
            if (j >= seq[i]) {
                dp[t][j] = min(dp[t][j], Min - C*j + pow2(j-seq[i]));
            }
        }
    }
    Min = INF;
    for (int i = seq[N]; i <= 100; ++i) {
        Min = min(Min, dp[N&1][i]);
    }
    return Min;
}

int main() {
    while (scanf("%d %d", &N, &C) == 2) {
        for (int i = 1; i <= N; ++i) {
            scanf("%d", &seq[i]);
        }
        printf("%d\n", DP());
    }
    return 0;    
}

 

posted @ 2013-01-09 16:49  沐阳  阅读(381)  评论(0编辑  收藏  举报