【数字游戏】noip2003普及组

题目描述:
丁丁最近沉迷于一个数字游戏之中。这个游戏看似简单,但丁丁在研究了许多天之后却发觉原来在简单的规则下想要赢得这个游戏并不那么容易。游戏是这样的,在你面前有一圈整数(一共n个),你要按顺序将其分为m个部分,各部分内的数字相加,相加所得的m个结果对10取模后再相乘,最终得到一个数k。游戏的要求是使你所得的k最大或者最小。 例如,对于下面这圈数字(n=4,m=2):
在这里插入图片描述
当要求最小值时,((2-1) mod 10)×((4+3) mod 10)=1×7=7,要求最大值时,为((2+4+3) mod 10)×(-1 mod 10)=9×9=81。特别值得注意的是,无论是负数还是正数,对10取模的结果均为非负值。 丁丁请你编写程序帮他赢得这个游戏。

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

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

样例输入
4 2
4
3
-1
2

样例输出
7
81

*emmmm…这应该是一道区间dp,虽然用dfs也能做 *。将n开两倍,然后把环做成一条链,就是一道典型的区间dp了。这道题只需定义一个三维数组做区间dp,更新求最大值与最小值即可。具体状态转移方程如下

max[i][j][len]=max(ma[i][j][len],ma[i][k-1][len-1]*h);
min[i][j][len]=min(mi[i][j][len],mi[i][k-1][len-1]*h);

max[][][]代表最大值,min[][][]代表最小值;
i是当前区间的头,j是尾;k是区间中的某个位置;
h是根据题目得到的m个部分中的一个部分的总和;
len是指当前的第len个部分;
具体代码如下

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<queue>
#include<vector>
#include<stack>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#define ull int
#define re register
using namespace std;
ull n;
ull m;
ull a[101];
ull dis1[101][101][10];
ull dis2[101][101][10];
ull qian[101];//开数组求前缀和,也可直接用a[]求前缀和;
inline ull max(ull x,ull y){
    if(x<y)x=y;
    return x;
}//自行定义max函数
inline ull min(ull x,ull y){
    if(x>y)x=y;
    return x;
}//自行定义min函数
inline void init(){
    scanf("%d%d",&n,&m);
    for(re ull i=1;i<=n;i++){
        scanf("%d",&a[i]);
        a[i+n]=a[i];//在链上存入两遍数据,便于代码实现,即将环转成链
    }
    for(re ull i=1;i<=2*n;i++)
        qian[i]=qian[i-1]+a[i];//前缀和
    for(re ull i=1;i<=n*2;i++)
        for(re ull j=i;j<=2*n;j++)
            dis1[i][j][1]=dis2[i][j][1]=((qian[j]-qian[i-1])%10+10)%10;//第一部分直接处理;
    for(re ull i=2;i<=m;i++)
        for(re ull j=1;j<=n*2;j++)
            for(re ull k=j+i-1;k<=2*n;k++)
                dis2[j][k][i]=2147483647;//初始化最小值
    for(re ull s=2;s<=m;s++)
        for(re ull i=1;i<=n*2;i++)
            for(re ull j=i+s-1;j<=2*n;j++)
                for(re ull k=i+s-2;k<=j-1;k++){
                    dis1[i][j][s]=max(dis1[i][k][s-1]*(((qian[j]-qian[k])%10+10)%10),dis1[i][j][s]);/*求最大值*/
                    dis2[i][j][s]=min(dis2[i][k][s-1]*(((qian[j]-qian[k])%10+10)%10),dis2[i][j][s]);/*dp求最小值*/
                }
    ull ans1=0;
    ull ans2=2147483647;
    for(re ull i=1;i<=n;i++){
        ans1=max(ans1,dis1[i][i+n-1][m]);//求最大值
        ans2=min(ans2,dis2[i][i+n-1][m]);//求最小值
    }
    cout<<ans2<<endl<<ans1;
}
int main(){
    init();
    return 0;
}

这道题确实用dfs做,有兴趣的朋友可以去落谷看dfs题解,我这里就不打了;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值