1. 程式人生 > >P1043 數字遊戲

P1043 數字遊戲

一行 namespace include 狀態轉移方程 [0 scan void 描述 pri

P1043 數字遊戲

題目描述

丁丁最近沈迷於一個數字遊戲之中。這個遊戲看似簡單,但丁丁在研究了許多天之後卻發覺原來在簡單的規則下想要贏得這個遊戲並不那麽容易。遊戲是這樣的,在你面前有一圈整數(一共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

思路:

思路一:

狀態:

把環拆成鏈,枚舉開始位置(O(n)^4)

d[i][j]表示前i個數分成j部分的最優值

狀態轉移方程:

d[i][j]=opt(d[i][j],d[k][j-1]*(((s[i]-s[k])%10+10)%10))

s[i]是前i個數的和

邊界:

d[0][0]=1;

d[i][1]=((s[i]%10+10)%10)

思路二: 把環看成兩倍的線性長 (O(n)^3) 思路三: %n 對n取模 (O(n)^3) 這種的話狀態轉移方程f[i][j],i表示起點,j表示長度
 1 #include<cstdio>
 2 #include<algorithm>
 3 using namespace std;
 4 
 5 int n,m,Max,Min,s[55],d[55][10],dp[55][10];
 6 void DP(int a[])
 7 {
 8     int i,j,k;
9 for(i=1;i<=n;i++) 10 s[i]=s[i-1]+a[i]; 11 for(i=0;i<=n;i++) 12 for(j=0;j<=m;j++) 13 { 14 d[i][j]=0; 15 dp[i][j]=-1u>>1; 16 } 17 d[0][0]=dp[0][0]=1; 18 for(i=1;i<=n;i++) 19 d[i][1]=dp[i][1]=(s[i]%10+10)%10; 20 for(j=2;j<=m;j++) 21 for(i=j;i<=n;i++) 22 for(k=j-1;k<i;k++) 23 { 24 d[i][j]=max(d[i][j],d[k][j-1]*(((s[i]-s[k])%10+10)%10));//第k+1個數到第i個數分到第j部分 25 dp[i][j]=min(dp[i][j],dp[k][j-1]*(((s[i]-s[k])%10+10)%10)); 26 } 27 Max=max(Max,d[n][m]); 28 Min=min(Min,dp[n][m]); 29 } 30 int main() 31 { 32 while(scanf("%d%d",&n,&m)==2) 33 { 34 int i,j,a[105]; 35 Max=0; 36 Min=-1u>>1; 37 for(i=1;i<=n;i++) 38 { 39 scanf("%d",a+i); 40 a[n+i]=a[i]; 41 } 42 for(j=0;j<n;j++) 43 DP(a+j); 44 printf("%d\n%d\n",Min,Max); 45 } 46 }

P1043 數字遊戲