• NOIP 2003 数字游戏


    题目

    题目描述

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

    例如,对于下面这圈数字( n=4,m=2n=4,m=2n=4,m=2 ):

    要求最小值时, ((2−1)mod10)×((4+3)mod10)=1×7=7((2-1) mod 10)×((4+3) mod 10)=1×7=7((21)mod10)×((4+3)mod10)=1×7=7 ,要求最大值时,为 ((2+4+3)mod10)×(−1mod10)=9×9=81((2+4+3) mod 10)×(-1 mod 10)=9×9=81((2+4+3)mod10)×(1mod10)=9×9=81 。特别值得注意的是,无论是负数还是正数,对 101010 取模的结果均为非负值。

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

    输入输出格式

    输入格式:

    输入文件第一行有两个整数, n(1≤n≤50)n(1≤n≤50)n(1n50) 和 m(1≤m≤9)m(1≤m≤9)m(1m9) 。以下 nnn 行每行有个整数,其绝对值 ≤104 le 10^4104 ,按顺序给出圈中的数字,首尾相接。

    输出格式:

    输出文件有 222 行,各包含 111 个非负整数。第 111 行是你程序得到的最小值,第 222 行是最大值。

    输入输出样例

    输入样例#1:
    4 2
    4
    3
    -1
    2
    
    输出样例#1: 
    7
    81
    

    分析

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

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

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

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

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

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

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

    f[i][j][p+q] = max f[i][j][p+q−1]∗(S[j]−S[k])

    最后的公式长这样:

    fmx[l][r][i] = max(fmx[l][r][i], fmx[l][k][i-1]*mod(S[r]-S[k]));

    程序

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 int fmx[200][200][20], fmn[200][200][20], n, m, S[150], a[150], ans_min, ans_max;
     4 int mod(int x)
     5 {
     6     return (x % 10 + 10) % 10;
     7 }
     8 int main()
     9 {
    10     cin >> n >> m;
    11     for (int i = 1; i <= n; i++)
    12     {
    13         cin >> a[i];
    14         a[i+n] = a[i];
    15     }
    16     for (int i = 1; i <= n*2; i++)
    17         S[i] = S[i-1] + a[i];
    18     for (int i = 1; i <= (n<<1); i++)
    19         for (int j = i; j <= (n<<1); j++)
    20             fmx[i][j][1] = fmn[i][j][1] = mod(S[j]-S[i-1]);
    21     for (int i = 2; i <= m; i++)
    22         for (int l = 1; l <= (n<<1); l++)
    23             for (int r = l+i-1; r <= l+n-1; r++)
    24             {
    25                 fmn[l][r][i] = 0x3F3F3F3F;
    26                 for (int k = l+i-2; k < r; k++)
    27                 {
    28                     fmx[l][r][i] = max(fmx[l][r][i], fmx[l][k][i-1]*mod(S[r]-S[k]));
    29                     fmn[l][r][i] = min(fmn[l][r][i], fmn[l][k][i-1]*mod(S[r]-S[k]));
    30                 }
    31             }
    32     ans_max = 0;
    33     ans_min = 0x3F3F3F3F;
    34     for (int i = 1; i <= n; i++)
    35     {
    36         ans_max = max(ans_max,fmx[i][i+n-1][m]);
    37         ans_min = min(ans_min,fmn[i][i+n-1][m]);
    38     }
    39     cout << ans_min << endl << ans_max << endl;
    40     return 0;
    41 }
     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 const int MAXN = 150;
     4 int min_ans = 10000000, max_ans, n, m, S[MAXN], a[MAXN];
     5 int power[10] = {1, 9, 81, 729, 6561, 59049, 531441, 4782969, 43046721, 387420489};
     6 void dfs(int dep, int now, int start, int x)
     7 {
     8     if (dep > m)
     9     {
    10         x = x*((S[start+n-1]-S[now-1]+1000000)%10);
    11         min_ans = min(min_ans, x);
    12         max_ans = max(max_ans, x);
    13     }
    14     if (x >= min_ans && x * power[m-dep+2] <= max_ans)
    15         return;
    16     if (dep > m || now > start+n-1)
    17         return;
    18     for (int i = now; i <= start+n-2-(m-dep); i++)
    19         dfs(dep+1, i+1, start, max(1,x)*((S[i]-S[now-1]+1000000)%10));
    20 }
    21 int main()
    22 {
    23     S[0] = 0;
    24     cin >> n >> m;
    25     m--;
    26     for (int i = 1; i <= n; i++)
    27     {
    28         cin >> a[i];
    29         a[n+i] = a[i];
    30     }
    31     for (int i = 1; i <= 2*n; i++)
    32         S[i] = S[i-1] + a[i];
    33     if (!m)
    34     {
    35         cout << (S[n] % 10 + 10) % 10 << endl << (S[n] % 10 + 10) % 10 << endl;
    36         return 0;
    37     }
    38     for (int i = 1; i <= n; i++)
    39         dfs(1,i,i,0);
    40     cout << min_ans << endl << max_ans << endl;
    41     return 0;
    42 }
  • 相关阅读:
    JVM 垃圾回收器工作原理及使用实例介绍(转载自IBM),直接复制粘贴,需要原文戳链接
    装tomcat和nginx心得
    jms的俩种模式
    裸奔Spring(1)
    一个最小mybatis
    SpringBoot和数据库连接
    SpringBoot的基础Pom
    SpringBoot读取配置文件
    埃拉托斯特尼素数筛法
    hdu 1175 连连看
  • 原文地址:https://www.cnblogs.com/OIerPrime/p/9291842.html
Copyright © 2020-2023  润新知