【動態規劃】整數劃分

如何把一个正整数N(N长度<20)划分为M(M>1)个部分,使这N个部分的乘积最
大。N、M从键盘输入,输出最大值及一种划分方式。 
输入数据: 
    第一行一个正整数T(T<=10000),表示有T 组数据。 
    接下来T行每行两个正整数N,M。 
 
输出数据: 
对于每组数据 
第一行输出最大值。 
第二行输出划分方案,将N 按顺序分成M个数输出,两个数之间用空格格开。 
 
样例 
  输入文件:separate.in 
1 
199 2 
输出文件:separate.out 
171 
19 9 
方法很簡單,有一些注意事項。
狀態:用f[i][j]表示將前i位數分成j個部分的最大乘積。
轉移方程:f[i][j] = max(f[k - 1][j - 1] * c[k][i])。(一定要注意k的範圍是j <= k <= i,調試的時候就因為這個錯誤調了半天。)
每次數組要清零,並且f數組最開始最好賦為-1,使f數組的值必有改變,保證記錄方案的g數組能記錄到相應的k值。
注意數據類型是long long,特別是輸出的時候。
ACCode:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <bitset>

using std::max;

typedef long long LL;
const char fi[] = "separate.in";
const char fo[] = "separate.out";
const int maxN = 30;
const int MAX = 0x3fffff00;
const int MIN = -MAX;

LL f[maxN][maxN];
int g[maxN][maxN];
LL c[maxN][maxN];
int n, m, T;
LL N;

  void init_file()
  {
    freopen(fi, "r", stdin);
    freopen(fo, "w", stdout);
  }
  
  void print(int i, int j)
  {
    if (i <= 0 || j <= 0) return;
    print(g[i][j] - 1, j - 1);
    printf("%I64d ", c[g[i][j]][i]);
  }
  
  void work()
  {
    scanf("%d", &T);
    for (int t = 0; t < T; ++t)
    {
      memset(f, 0xff, sizeof(f));
      memset(g, 0, sizeof(g));
      memset(c, 0, sizeof(c));
      scanf("%I64d%d", &N, &m);
      n = 0;
      for (LL tmp = N; tmp; tmp /= 10) ++n;
      for (int i = n; i > 0; --i)
        {c[i][i] = N % 10; N /= 10; }
      for (int i = 1; i < n + 1; ++i)
       for (int j = i + 1; j < n + 1; ++j)
        c[i][j] = c[i][j - 1] * 10 + c[j][j];
      for (int i = 1; i < n + 1; ++i)
        {f[i][1] = c[1][i]; g[i][1] = 1; }
      for (int i = 2; i < n + 1; ++i)
       for (int j = 2; j < i + 1; ++j)
        for (int k = j; k < i + 1; ++k)
	//注意k的枚舉範圍!!
          if (f[k - 1][j - 1] * c[k][i] > f[i][j])
          {
            f[i][j] = f[k - 1][j - 1] * c[k][i];
            g[i][j] = k;
          }
      printf("%I64d\n", f[n][m]);
      print(n, m);
      printf("\n");
    }
  }
  
int main()
{
  init_file();
  work();
  exit(0);
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值