NOJ 1017 乘機最大 區間dp


乘積最大

時間限制(普通/Java) : 1000 MS/ 3000 MS          運行內存限制 : 65536 KByte
總提交 : 518            測試通過 : 256 

比賽描述

今年是國際數學聯盟確定的“2000——世界數學年又恰逢我國著名數學家華羅庚先生誕辰90週年。在華羅庚先生的家鄉江蘇金壇,組織了一場別開生面的數學智力競賽的活動,你的一個好朋友XZ也有幸得以參加。活動中,主持人給所有參加活動的選手出了這樣一道題目:

    設有一個長度爲N的數字串,要求選手使用K個乘號將它分成K+1個部分,找出一種分法,使得這K+1個部分的乘積能夠爲最大。

    同時,爲了幫助選手能夠正確理解題意,主持人還舉了如下的一個例子:

有一個數字串:312 N=3K=1時會有以下兩種分法:

1)  3*12=36

2)  31*2=62

這時,符合題目要求的結果是:31*2=62

現在,請你幫助你的好朋友XZ設計一個程序,求得正確的答案。



輸入

輸入共有兩行:

第一行共有2個自然數NK6≤N≤401≤K≤6

第二行是一個長度爲N的數字串。

輸出

輸出所求得的最大乘積(一個自然數),答案在long long 數據範圍之內。

樣例輸入

4  2
1231

樣例輸出

62

題目來源

NOIP 2000


    題意:給出一個長度爲n的數字串,在數字串之間插入k個乘號,求構成的k+1個部分形成的整數的積最大。

    分析:該題是區間dp類的題目。需要注意的是該題是要將k個乘號全部用到,而不是用不大於k個乘號得到最大值,一開始會錯意導致錯誤。該題可以建立dp[i][j]表示的是從0-i個數字使用j個乘號得到的最大值,dp[i][j]=max(dp[k][j-1]*res(k+1,i)),其中res代表的是從第K+1個數字到第i個數字形成的一個整數的值,相當於從用到了j-1的值再向後添加一個乘號,乘上k+1到i組成的那個數。這一點需要好好理解。寫的代碼是從0下標開始計數的,建議從一下標開始計數比較方便理解。同時可以將計算形成數的函數抽象出來,可以大大增強代碼的可讀性,還有很大改進空間,在此給出AC代碼:

//寫成了 使用不大於k個乘號得到 的最大值
//該題需要的是不需使用到 k個乘號
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int maxn=45;
char s[maxn];
int a[maxn];
long long dp[maxn][8];//dp[i][j]前 i個字符 用j個乘號得到的最大數
int main()
{
	int n,k;
	while(~scanf("%d%d",&n,&k))
	{
		scanf("%s",s);
		for(int i=0; i<n; i++)
			for(int j=0; j<=k; j++)
				dp[i][j]=-1;
		for(int i=0; i<n; i++)
			a[i]=s[i]-'0';
		for(int i=0; i<=k; i++)//處理第一行
			dp[0][i]=a[0];
		long long temp=a[0];
		for(int i=0; i<n-1; i++)	//處理第一列
		{
			dp[i][0]=temp;
			temp*=10;
			temp+=a[i+1];
		}
		dp[n-1][0]=temp;
		for(int i=0; i<n; i++)
			for(int j=1; j<=k; j++)//對每個dp[i][j]進行判斷
			{
				//printf("%d %d \n",i,j);
				for(int m=j-1; m<=i; m++)//需要塞下j-1個乘號的話 至少需要j個數
					//由於使用的下標從0開始 所以此處循環起始下標爲j-1
				{

					int t=0;
					for(int p=m+1; p<=i; p++)
					{
						t+=a[p];
						t*=10;
					}
					t/=10;//計算形成數
					if(t)
						dp[i][j]=max(dp[i][j],t*dp[m][j-1]);
				}
			}
		printf("%lld\n",dp[n-1][k]);
	}
}
    區間dp需要多加練習。

    特記下,以備後日回顧。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章