1. 程式人生 > >P1043 數字遊戲-動態規劃,區間dp

P1043 數字遊戲-動態規劃,區間dp

丁丁最近沉迷於一個數字遊戲之中。這個遊戲看似簡單,但丁丁在研究了許多天之後卻發覺原來在簡單的規則下想要贏得這個遊戲並不那麼容易。遊戲是這樣的,在你面前有一圈整數(一共n個),你要按順序將其分為m個部分,各部分內的數字相加,相加所得的m個結果對10取模後再相乘,最終得到一個數kk。遊戲的要求是使你所得的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取模的結果均為非負值。

丁丁請你編寫程式幫他贏得這個遊戲。

剛看這道題感覺就像是石子合併一樣的水題;

但是寫著寫著感覺有些不對勁,因為這題需要分段,一段一段的取模,而石子合併不需要取模,所以順序無所謂;

這樣的話就只能再加一維表示分段的數量;

初始化:

首先把min,max的每個區間只分1段的值預處理出來;

之後m>2的情況,max無所謂本身就是0,min的話要把陣列初始化為無窮大;

狀態轉移方程:

f1[l][r][i]=min(f1[l][r][i],f1[l][k][i-1]*mod(num[r]-num[k]));

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

這樣不就好啦~~~

最後一點:畢竟是化環為鏈了,要以長度為n重新掃一遍,取min,max。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define inf 2147483647
using namespace std;
int n,m;
int f1[101][101][11],f2[101][101][11];
int a[1001],num[1001];
int mod(int x)
{
    return ((x%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++){
        num[i]=num[i-1]+a[i];
    }
    for(int l=1;l<=2*n;l++){
        for(int r=l;r<=2*n;r++){
            f1[l][r][1]=f2[l][r][1]=mod(num[r]-num[l-1]);
        }
    }
    for(int i=2;i<=m;i++){
        for(int l=1;l<=2*n;l++){
            for(int r=l+i-1;r<=2*n;r++){
                f1[l][r][i]=inf;
            }
        }
    }
    for(int i=2;i<=m;i++){
        for(int l=1;l<=2*n;l++){
            for(int r=l+i-1;r<=2*n;r++){
                for(int k=l+i-2;k<r;k++){
                    f1[l][r][i]=min(f1[l][r][i],f1[l][k][i-1]*mod(num[r]-num[k]));
                    f2[l][r][i]=max(f2[l][r][i],f2[l][k][i-1]*mod(num[r]-num[k]));
                }
            }
        }
    }
    int maxn=0,minn=inf;
    for(int i=1;i<=n;i++){
        maxn=max(maxn,f2[i][i+n-1][m]);
        minn=min(minn,f1[i][i+n-1][m]);
    }
    printf("%d\n%d",minn,maxn);
    return 0;
}