P1043 [NOIP2003 普及组] 数字游戏 题解(区间dp)

转载 :https://www.luogu.com.cn/blog/zhoushuyu/solution-p1043

题目链接

题目思路

总结一下这种类似DP题目的思路和技巧吧。

1、破环成链。没有太多的技巧性,具体而言就是把数据存储两遍,使得环形的数据可以链式展开,便于我们去DP。

但最后一定要记得扫一遍答案,取\(F[i][i+N-1]\; i:1->N\)中的最大/小值。

2、前缀和。这个东西并不是在所有情况下都适用,但使用起来真的很方便,可以把O(n)的复杂度优化为O(1)。不过只适用于需要把数据直接相加的地方,比如说这道题。

3、初始化。这里实际上包括两点,一方面是在某些特殊情况下需要初始化,初始化为某特定值(比如本题只分成1段的时候)。另一方面也就是数组初始化,求最大值的时候根本不用管(因为初始默认为0),在求最小值的时候把数组全部赋初值为极大值就好啦。

4、状态表达。一般来说可以用F[i][j]表示在区间[i,j]中怎么怎么样,但由于本题还加了一个分为几段的状态,就把数组直接加一维就好了。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long  ll;
#define fi first
#define se second
const int maxn=1e2+5,inf=0x3f3f3f3f;
int n,m;
int a[maxn];
int dpma[maxn][maxn][10+5];
int dpmi[maxn][maxn][10+5];
int mod(int a){
   return (a%10+10)%10;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){//破环成链
        scanf("%d",&a[i]);
        a[i+n]=a[i];
    }
    for(int i=1;i<=2*n;i++){ //前缀和
        a[i]+=a[i-1];
    }
    memset(dpmi,0x3f,sizeof(dpmi));
    for(int i=1;i<=m;i++){
        for(int j=1;j<=2*n;j++){
            dpmi[j][j][i]=dpma[j][j][i]=mod(a[j]-a[j-1]);
        }
    }
    for(int i=1;i<=m;i++){
        for(int r=1;r<=2*n;r++){// 右端点
            for(int l=r;l>=1;l--){// 左端点
                if(i==1){
                    dpma[l][r][i]=dpmi[l][r][i]=mod(a[r]-a[l-1]);
                    continue;
                }
                for(int mid=l;mid<r;mid++){ //中间端点
                    dpma[l][r][i]=max(dpma[l][r][i],dpma[l][mid][i-1]*mod(a[r]-a[mid])); //[mid+1,r]
                    dpmi[l][r][i]=min(dpmi[l][r][i],dpmi[l][mid][i-1]*mod(a[r]-a[mid]));
                }
            }
        }
    }
    int ans1=inf,ans2=0;
    for(int i=1;i<=n;i++){
        ans1=min(ans1,dpmi[i][i+n-1][m]);
        ans2=max(ans2,dpma[i][i+n-1][m]);
    }
    printf("%d\n%d\n",ans1,ans2);
    return 0;
}

上一篇:洛谷P1044 [NOIP2003 普及组] 栈(DP、卡特兰数、递推)


下一篇:每天半小时:入门Python,Pandas库十套练习题带你学会数据分析(二)