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] dp[i][i+d][k]为合并 [ i , i + d ] [i, i + d] [i,i+d]区间为 k 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]) k=1:dp[i][i+d][k]=min(dp[i][j][x1]+dp[i][j+1][1]+sum[i][i+d])我们在这里预处理合并成一堆的情况,x属于 [ l , r ] [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=2:dp[i][i+d][k]=min(dp[i][i+d][k],dp[i][j][k1]+dp[j+1][i+d][1])

k为1的时候需要预处理,因为后面的所有状态都是由合并成一堆的情况转移过去的,在合并为1堆的时候需要满足堆数在 [ l , r ] [l, r] [l,r]区间内,后面的转移无需再次考虑。


a c   c o d e : ac\ code: ac code:

#include 
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;
}

你可能感兴趣的:(ICPC,区间dp)