DP算法- 循环取石子

//
// 有N堆(N<=100)石子围成一圈, 每堆石子数量,现要将石子有序的合并成一堆,规定如下:每次只能合并相邻的2堆石子,合并花费为新合成的一堆石子的数量。求将这N堆石子合并成一堆的总花费最小(或最大)。
//
// 状态转移方程dp[i][j] = min(dp[i][k]+[(i+k+1)%n][j-k-1]+sum[i][j])
// 把要合并的j堆分成前k堆和后j-k-1堆以及最后一堆i+j

//input:
//3
//4
//4 5 9 4
//6
//3 4 6 5 4 2
//10
//9 3 2 8 5 10 3 13 2 4
//output
//Case #1: 43 54
//Case #2: 61 91
//Case #3: 188 368

#include <stdio.h>

static const int MAX_N = 105;
static const int INF = 1 << 30;

int n;  // number of piles
int piles[MAX_N];

int mins[MAX_N][MAX_N]; // i是堆的编号,0<=i<n, 从第i堆开始合并后面j个堆的最小花费。
int maxs[MAX_N][MAX_N];


int min(int a, int b)
{
 return a < b ? a : b;
}

int max(int a, int b)
{
 return a > b ? a : b;
}

// 计算从编号为start的堆开始,统计length个堆的总和
int getSum(int start, int length)
{
 int sum = piles[start];
 for (int i = 1; i <= length; i++)
 {
  int j = (start+i)%n;
  sum += piles[j];
 }

 return sum;
}

int solve()
{
 int i, j, k;
 // j为0时,没有合并动作,花费为0
 for (i = 0; i <= n - 1; i++)
 {
  mins[i][0] = 0;
  maxs[i][0] = 0;
 }
 // want the merge j piles with ith pile. 1<=j<=n-1
 for (j = 1; j <= n - 1; j++)
 {
  // start from ith pile
  for (i = 0; i <= n-1; i++)
  {
   // let the mins[i][j] is INF
   mins[i][j] = INF;
   maxs[i][j] = 0;
   //printf("sum[%d][%d]=%d\n", i, j, getSum(i, j));
   // k is the subset of j, that is 0~j-1
   for (k = 0; k <= j - 1; k++)
   {
    //printf("i:%d j:%d k:%d i+k+1:%d j-k-1:%d\n", i, j, k, (i + k + 1)%n, (j - k - 1));
    mins[i][j] = min(mins[i][j], mins[i][k] + mins[(i+k+1)%n][j-k-1]+getSum(i, j));
    maxs[i][j] = max(maxs[i][j], maxs[i][k] + maxs[(i + k + 1) % n][j - k - 1] + getSum(i, j));
   }
   //printf("min[%d][%d]=%d\n", i, j, mins[i][j]);
  }
  //printf("-------------------------------\n");
 }
 return 0;
}

int main()
{
 freopen("stone1_input.txt", "r", stdin);
 int T;
 scanf("%d", &T);
 int tc;
 for (tc = 1; tc <= T; tc++)
 {

  scanf("%d", &n);
  int i;
  for (i = 0; i < n; i++)
  {
   scanf("%d", &piles[i]);
  }
  int minval = INF;
  int maxval = 0;

  solve();

  for (i = 0; i < n; i++)
  {
   if (minval > mins[i][n - 1])
    minval = mins[i][n - 1];
   if (maxval < maxs[i][n - 1])
    maxval = maxs[i][n - 1];
  }
  printf("Case #%d: %d %d\n", tc, minval, maxval);
 }
 return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值