题目描述

丁丁最近沉迷于一个数字游戏之中。这个游戏看似简单,但丁丁在研究了许多天之后却发觉原来在简单的规则下想要赢得这个游戏并不那么容易。游戏是这样的,在你面前有一圈整数(一共n个),你要按顺序将其分为m个部分,各部分内的数字相加,相加所得的m个结果对10取模后再相乘,最终得到一个数k。游戏的要求是使你所得的k最大或者最小。
例如,对于下面这圈数字(n=4,m=2):

当要求最小值时,,要求最大值时,为。特别值得注意的是,无论是负数还是正数,对10取模的结果均为非负值。

丁丁请你编写程序帮他赢得这个游戏。

输入描述:

第一行有两个整数,n(1≤n≤50)和m(1≤m≤9)。
以下n行每行有个整数,其绝对值不大于104,按顺序给出圈中的数字,首尾相接。

输出描述:

包括两行,各包含一个非负整数。
第一行是你程序得到的最小值;
第二行是最大值。

示例1

输入
4 2
4
3
-1
2
输出
7
81

解答

这题用了两个方法来做,第一个做出来的是DFS,就先讲讲搜索的做法。

开始之前要先说这道题的基本思路,很显然这道题是和环形问题和有关的,那么我们对于环形一类的问题有一个通用做法:断环为链(或者类似的叫法,whatever)。也就是把整个数组复制一遍,然后以每一个点作为起点,及其以后的个点,作为范围,其实相当于,我们用一个1到n的循环先指定了这个数组第一个断开的地方。

这道题是可以用深搜来做的,但是要保持时刻的头脑清晰才行,有点略微搞脑子。

搜索函数包含了四个参数值,我分别称为dep, now, start, x。分别代表了,接下来要切割的次数(搜索的深度或者层数)、当前处理到的位置、当前情况链的初始点、当前值。重点在于剪枝。这道题是一个基础的可行性剪枝(一开始怎么没想到……),简单来说,如果当前搜索到的值已经不可能再给这道题目带来更优的值(最大/最小值),那么剪枝,舍去接下来的搜索,否则搜索会变成一个庞大的量。递归的时候,我们在now之后的点里面找符合条件的点作为下一个切割点,但也要注意这个点要使得之后的切割点还有地方放(循环的判断)。

最后的最后,有一个我把数据拿下来跑才找到的大坑,不然估计这辈子都过不了……当切割次数为1的时候,实际上就是求和。但如果正常进入循环搜索,就会因为剩余切割次数为0而直接跳出。处理方法两种,把判断往后拿,拿到函数最后,或者是在程序一开始就特判m的值。

另外一个就是DP,DP的程序难度不大,长度跟DFS也差不多。思考DP可以说是一劳永逸,只要写出了公式,之后就是打打代码的事情。所以重点说说这个公式。题解里有很多人写五层循环,其实没有必要,我们可以略微简化一点。

简单来说,不用枚举两侧分成了多少个部分,因为这会被之前或之后的循环枚举到,打个比方:如果枚举到了左端点是 i,右端点是 j,断点是 k,左边分成了p 个部分,右边分成了 q 个部分。
最后的公式长这样:

程序

DFS
 #include <bits/stdc++.h>
  using namespace std;
  int fmx[200][200][20], fmn[200][200][20], n, m, S[150], a[150], ans_min, ans_max;
  int mod(int x)
  {
     return (x % 10 + 10) % 10;
  }
  int main()
 {
     cin >> n >> m;
     for (int i = 1; i <= n; i++)
      {
          cin >> a[i];
          a[i+n] = a[i];
      }
      for (int i = 1; i <= n*2; i++)
         S[i] = S[i-1] + a[i];
      for (int i = 1; i <= (n<<1); i++)
         for (int j = i; j <= (n<<1); j++)
             fmx[i][j][1] = fmn[i][j][1] = mod(S[j]-S[i-1]);
      for (int i = 2; i <= m; i++)
          for (int l = 1; l <= (n<<1); l++)
              for (int r = l+i-1; r <= l+n-1; r++)
             {
                  fmn[l][r][i] = 0x3F3F3F3F;
                  for (int k = l+i-2; k < r; k++)
                 {
                      fmx[l][r][i] = max(fmx[l][r][i], fmx[l][k][i-1]*mod(S[r]-S[k]));
                      fmn[l][r][i] = min(fmn[l][r][i], fmn[l][k][i-1]*mod(S[r]-S[k]));
                  }
             }
      ans_max = 0;
      ans_min = 0x3F3F3F3F;
      for (int i = 1; i <= n; i++)
      {
          ans_max = max(ans_max,fmx[i][i+n-1][m]);
         ans_min = min(ans_min,fmn[i][i+n-1][m]);
     }
      cout << ans_min << endl << ans_max << endl;
     return 0;
 }
DP
 #include <bits/stdc++.h>
  using namespace std;
 const int MAXN = 150;
  int min_ans = 10000000, max_ans, n, m, S[MAXN], a[MAXN];
  int power[10] = {1, 9, 81, 729, 6561, 59049, 531441, 4782969, 43046721, 387420489};
 void dfs(int dep, int now, int start, int x)
 {
      if (dep > m)
      {
         x = x*((S[start+n-1]-S[now-1]+1000000)%10);
          min_ans = min(min_ans, x);
          max_ans = max(max_ans, x);
     }
     if (x >= min_ans && x * power[m-dep+2] <= max_ans)
          return;
      if (dep > m || now > start+n-1)
         return;
      for (int i = now; i <= start+n-2-(m-dep); i++)
         dfs(dep+1, i+1, start, max(1,x)*((S[i]-S[now-1]+1000000)%10));
  }
  int main()
 {
      S[0] = 0;
     cin >> n >> m;
     m--;
      for (int i = 1; i <= n; i++)
     {
         cin >> a[i];
         a[n+i] = a[i];
      }
      for (int i = 1; i <= 2*n; i++)
          S[i] = S[i-1] + a[i];
     if (!m)
     {
          cout << (S[n] % 10 + 10) % 10 << endl << (S[n] % 10 + 10) % 10 << endl;
         return 0;
     }
     for (int i = 1; i <= n; i++)
          dfs(1,i,i,0);
      cout << min_ans << endl << max_ans << endl;
      return 0;
  }



来源:OptimusPrime_L