1. 程式人生 > 其它 >C++-數字遊戲(NOIP 2003 PJT2) 解題思路

C++-數字遊戲(NOIP 2003 PJT2) 解題思路

【Horn Studio】程式設計專欄:數字遊戲(NOIP 2003 PJT2)解題思路

題目

題目描述

丁丁最近沉迷於一個數字遊戲之中。這個遊戲看似簡單,但丁丁在研究了許多天之後卻發覺原來在簡單的規則下想要贏得這個遊戲並不那麼容易。遊戲是這樣的,在你面前有一圈整數(一共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,按順序給出圈中的數字,首尾相接。保證答案在int範圍內。

輸出

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

樣例輸入 複製

4 2
4
3
-1
2

樣例輸出 複製

7
81

提示

來源

思路

這道題是一道區間DP題,跟能量項鍊還有石子合併差不多,我們用f[i][j][l]表示處理i到j這一區間分為l段所能得到的最大值或是最小值。

對於DP,只要寫出了公式,之後就是打打程式碼的事情。

簡單來說,不用列舉兩側分成了多少個部分,因為這會被之前或之後的迴圈列舉到,

並且我們同時需要最大值和最小值,只需寫兩個完全相反的程式碼。

for(int k=i;k<j;k++) {
              f[i][j][l]=max(f[i][j][l],f[i][k][l-1]*total[k+1][j]);
           if(g[i][k][l-1]<0x3F3F3F3F) 
               g[i][j][l]=min(g[i][j][l],g[i][k][l-1]*total[k+1][j]);
}

這道題的min陣列需要注意一下,必須要寫0x3F3F3F3F,這就是無窮大,也不用擔心會溢位。

程式碼

#include<bits/stdc++.h>
using
namespace std; int n,m,a[130],f[130][130][100],g[130][130][100],sum[130]; int minans=0x3F3F3F3F,maxans=-1,total[100][100]; int main(){ cin>>n>>m; for(int i=1;i<=n;++i) { cin>>a[i]; a[i+n]=a[i]; } n<<=1; memset(g,0x3F3F3F3F,sizeof(g)); for(int i=1;i<=n;++i) sum[i]=sum[i-1]+a[i]; for(int i=1;i<=n;i++) for(int j=i;j<=n;j++) f[i][j][1]=g[i][j][1]=total[i][j]=((sum[j]-sum[i-1])%10+10)%10; for(int l=2;l<=m;l++) for(int j=1;j<=n;j++) for(int i=1;i<=j;i++) for(int k=i;k<j;k++) { f[i][j][l]=max(f[i][j][l],f[i][k][l-1]*total[k+1][j]); if(g[i][k][l-1]<0x3F3F3F3F) g[i][j][l]=min(g[i][j][l],g[i][k][l-1]*total[k+1][j]); } n>>=1; for(int i=1;i<=n;++i){ maxans=max(maxans,f[i][i+n-1][m]); minans=min(minans,g[i][i+n-1][m]); } cout<< minans<<endl<<maxans; }

彩蛋

哈嗨嗨!