天天看點

luogu 1043 數字遊戲(區間dp)

​​http://www.elijahqi.win/archives/512​​​

題目描述

丁丁最近沉迷于一個數字遊戲之中。這個遊戲看似簡單,但丁丁在研究了許多天之後卻發覺原來在簡單的規則下想要赢得這個遊戲并不那麼容易。遊戲是這樣的,在你面前有一圈整數(一共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行每行有個整數,其絕對值不大于104,按順序給出圈中的數字,首尾相接。

輸出格式:

輸出檔案有兩行,各包含一個非負整數。第一行是你程式得到的最小值,第二行是最大值。

輸入輸出樣例

輸入樣例#1:

4 2

4

3

-1

2

輸出樣例#1:

7

81

這個真的超級坑啊,取Mod和c++裡的取餘不一樣,仔細讀題的話,可以發現結果加10就可以了

fmin ,fmax分别表示最大和最小 fmin||fmax[i][j][k]表示i~j區間切成k塊的答案

使用了一個字首和來求答案

#include<cstdio>
#include<cstring>
#define inf 0x7fffffff
inline int work(int x){
    int tmp=x%10;if (tmp<0) tmp+=10;return tmp;
}
inline int max(int x,int y){
    return x>y?x:y;
}
inline int min(int x,int y){
    return x<y?x:y;
}
int fmin[110][110][10],fmax[110][110][10],a[110],n,m;
int main(){
    freopen("1043.in","r",stdin);
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;++i) scanf("%d",&a[i]),a[i+n]=a[i];int n1=n;n<<=1;
    for (int i=1;i<=n;++i) a[i]+=a[i-1];
    memset(fmin,0x7f,sizeof(fmin));
    for (int i=1;i<=n;++i)
        for (int j=i;j<=n;++j) fmin[i][j][1]=fmax[i][j][1]=work(a[j]-a[i-1]);
    for (int k=2;k<=m;++k)
        for (int i=1;i<=n;++i)
            for (int j=i+k-1;j<=n;++j)
                for (int z=i+k-2;z<j;++z){
                    fmin[i][j][k]=min(fmin[i][j][k],fmin[i][z][k-1]*work(a[j]-a[z]));
                    fmax[i][j][k]=max(fmax[i][j][k],fmax[i][z][k-1]*work(a[j]-a[z]));
                }
    int ans1=0,ans2=inf;
    for (int i=1;i<=n1;++i) ans1=max(fmax[i][i+n1-1][m],ans1),ans2=min(fmin[i][i+n1-1][m],ans2);
    printf("%d\n%d",ans2,ans1);
    return 0;
}