[Wikioi 1025][NOIP 2003普及組]數字遊戲
題目描述 Description
丁丁最近沉迷於一個數字遊戲之中。這個遊戲看似簡單,但丁丁在研究了許多天之後卻發覺原來在簡單的規則下想要贏得這個遊戲並不那麼容易。遊戲是這樣的,在你面前有一圈整數(一共n個),你要按順序將其分為m個部分,各部分內的數字相加,相加所得的m個結果對10取模後再相乘,最終得到一個數k。遊戲的要求是使你所得的k最大或者最小。
例如,對於下面這圈數字(n=4,m=2):
2
4 -1
3
當要求最小值時,((2-1) mod 10)×((4+3) mod 10)=1×7=7,要求最大值時,為((2+4+3) mod 10)×(-1 mod 10)=9×9=81。特別值得注意的是,無論是負數還是正數,對10取模的結果均為非負值。
丁丁請你編寫程式幫他贏得這個遊戲。
輸入描述 Input Description
輸入檔案第一行有兩個整數,n(1≤n≤50)和m(1≤m≤9)。以下n行每行有個整數,其絕對值不大於104,按順序給出圈中的數字,首尾相接。
輸出描述 Output Description
輸出檔案有兩行,各包含一個非負整數。第一行是你程式得到的最小值,第二行是最大值。
樣例輸入 Sample Input
4 2
4
3
-1
2
樣例輸出 Sample Output
7
81
資料範圍及提示 Data Size & Hint
en
題目思路
此題是一道環形DP題,不是很難,將環形圈斷開成單鏈,即將線性鏈條*2即可,具體如圖所示
然後從第一個點1開始,向右方DP,求出以第一個點為起點的DP最大值,最小值,DP完成後再從第2個點2開始,求出以第2個點為起點的DP最大值、最小值......依次直到起點到達環形終點第五個點5,求出所有最大值中的最最大值,最小值中的最最小值,這裡第i個點為起點的含義是第一個斷開的地方是第i個點與第i-1個點之間。
下面是程式碼:
#include <stdio.h>
#include <string.h>
#define MAX 10000000
#define MIN -10000000
int line[200],n,m,Min,Max,sum[200];
int f[200][20];//f[i][j]=第1-i個數分成j份,結果最大值
int g[200][20];//g[i][j]=第1-i個數分成j份,結果最小值
int min(int a,int b)
{
if(a>b)
return b;
return a;
}
int max(int a,int b)
{
if(a>b)
return a;
return b;
}
void dp(int a[])
{
int i,j,k;
for(i=1;i<=n;i++)
sum[i]=sum[i-1]+a[i];
for(i=0;i<=n;i++)
for(j=0;j<=m;j++)
{
f[i][j]=0;
g[i][j]=-1u>>1;
}
for(i=1;i<=n;i++)
{
f[i][1]=g[i][1]=(sum[i]%10+10)%10;
}
f[0][0]=1;
g[0][0]=1;
for(j=2;j<=m;j++)
{
for(i=j;i<=n;i++)
{
for(k=j-1;k<i;k++)
{
{
f[i][j]=max(f[i][j],f[k][j-1]*(((sum[i]-sum[k])%10+10)%10));
g[i][j]=min(g[i][j],g[k][j-1]*(((sum[i]-sum[k])%10+10)%10));
}
}
}
}
Max=max(Max,f[n][m]);
Min=min(Min,g[n][m]);
}
int main()
{
int i,j,k;
Max=0;
Min=-1u>>1;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
{
scanf("%d",&line[i]);
line[i+n]=line[i];
}
for(i=0;i<n;i++)
dp(line+i);
printf("%d\n%d\n",Min,Max);
return 0;
}