1. 程式人生 > >Pangu and Stones HihoCoder - 1636[區間dp]

Pangu and Stones HihoCoder - 1636[區間dp]

題意:有n個數字,每一次只能合併最少l個最多r個連續的數字,合併的代價為他們的和,詢問最小代價把他們合併成一堆,如果不能合併成一堆輸出0。


總結:比賽的時候只是想到了一點點區間dp,但是dp的含義沒有完全想明白,導致轉移方程寫不出來,然後卡到了最後,後來看了一下題解dp的含義頓悟了一點點,區間dp接觸的還是太少了。


題解:區間dp。
我們規定 d p [ i

] [ i + d ] [ k ] dp[i][i + d][k]
為合併 [ i , i + d ] [i, i + d]
區間為 k k 所需要的最小花費。然後得到下列轉移方程。

  • k = 1 : d p [ i ] [ i + d ] [ k ] = m i n ( d p [ i ] [ j ] [ x 1 ] + d p [ i ] [ j + 1 ] [ 1 ] + s u m [ i ] [ i + d ] ) k = 1: dp[i][i + d][k] = min(dp[i][j][x - 1] + dp[i][j + 1][1] + sum[i][i + d]) 我們在這裡預處理合併成一堆的情況,x屬於 [ l , r ] [l, r]
  • k = 2 : d p [ i ] [ i + d ] [ k ] = m i n ( d p [ i ] [ i + d ] [ k ] , d p [ i ] [ j ] [ k 1 ] + d p [ j + 1 ] [ i + d ] [ 1 ] ) k = 2: dp[i][i + d][k] = min(dp[i][i + d][k], dp[i][j][k - 1] + dp[j + 1][i + d][1])

k為1的時候需要預處理,因為後面的所有狀態都是由合併成一堆的情況轉移過去的,在合併為1堆的時候需要滿足堆數在 [ l , r ] [l, r] 區間內,後面的轉移無需再次考慮。


a c   c o d e : ac\ code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define met(a, b) memset(a, b, sizeof(a))
#define rep(i, a, b) for(int i = a; i <= b; i++)
#define per(i, a, b) for(int i = a; i >= b; i--)
#define fi first
#define se second
const int maxn = 1e3 + 10;
const int inf = 0x3f3f3f3f;
const double eps = 1e-8;
const int mod = 100003;

inline int read() {
   int x = 0, f = 1;
   char ch = getchar();
   while(ch < '0' || ch > '9') {if(ch == '0') f = -1; ch = getchar();}
   while(ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
   return f * x;
}

int n, l, r, a[maxn];
ll sum[102][102];

ll dp[102][102][102];

int main() {
   while(~scanf("%d%d%d", &n, &l, &r)) {
       rep(i, 0, 101) rep(j, 0, 101) rep(k, 0, 101) dp[i][j][k] = inf;
       met(sum, 0);
       rep(i, 1, n) a[i] = read();
       rep(i, 1, n) {
           rep(j, i, n) {
               sum[i][j] = sum[i][j - 1] + a[j];
               dp[i][j][j - i + 1] = 0;
           }
       }
       for(int d = 1; d <= n; d++) {
           for(int i = 1; i + d <= n; i++) {
               for(int j = i; j < i + d; j++) {
                   for(int k = l - 1; k <= r - 1; k++) {
                       dp[i][i + d][1] = min(dp[i][i + d][1], dp[i][j][k] + dp[j + 1][i + d][1] + sum[i][i + d]);
                   }
               }
               for(int k = 1; k <= n; k++) {
                   for(int j = i; j < i + d; j++) {
                       dp[i][i + d][k] = min(dp[i][i + d][k], dp[i][j][k - 1] + dp[j + 1][i + d][1]);
                   }
               }
           }
       }

       if(dp[1][n][1] == (ll)inf) puts("0");
       else printf("%lld\n", dp[1][n][1]);

   }

   return 0;
}