題目連結:https://www.rqnoj.cn/problem/311
題意:
給你一個長度為n的數字,用t個乘號分開,問你分開後乘積最大為多少。(6<=n<=40,1<=k<=30)
題解:
簡化問題:
給原數字之前新增一個"1 *",乘號不計入數量,對答案無影響。
例如:"1231"可以變成"(1*)1231"。
表示狀態:
dp[i][j] = max num(最後一個乘號之前的最大乘積)
i:此時在第i個數的前面添了一個乘號
j:用了j個乘號
例1:"(1*)12*31":
dp[2][1] = 12 (數位從0開始從左向右編號)
例2:"(1*)12*3*1"
dp[3][2] = 12*3 = 36
找出答案:
max dp[i][t] * cal_sec(i,n-1)
cal_sec(x,y)將數字串中[x,y]這個區間的字串轉化為數字
例如:設n=4,t=1.
此時為"(1*)12*31"
則此時這種方案的乘積為dp[2][1]* "31" = 12*31
如何轉移:
dp[i][j] = max dp[k][j-1] * cal_sec(k,i-1)
在前面的某一段乘積後面再續上一串數字,達到第i位,用了j個乘號。
前面的某一段乘積:列舉最後一個乘號在第k個數字之前,用了j-1個乘號。
要續的數字:從第k位到i-1位 = cal_sec(k,i-1)
邊界條件:
初始時用了0個乘號,但乘積為1。
例如:"(1*)1231".
特判:如果輸入的數字就是0,則直接返回0.
注:輸入用string,答案用long long存。
資料水。。。否則高精。。。
AC Code:
1 // state expression: 2 // dp[i][j] = max num 3 // i: last '*' is in front of ith bit 4 // j: used j '*' 5 // 6 // find the answer: 7 // max dp[i][t] * cal_sec(i,len-1) 8 // 9 // transferring: 10 // dp[i][j] = max dp[k][j-1] * cal_sec(k,i-1) 11 // 12 // boundary: 13 // if input == 0: return 0 14 // else dp[0] = 1, others = -1 15 #include <iostream> 16 #include <stdio.h> 17 #include <string.h> 18 #define MAX_N 45 19 #define MAX_K 35 20 21 using namespace std; 22 23 int n,t; 24 long long ans; 25 long long dp[MAX_N][MAX_K]; 26 long long sec[MAX_N][MAX_N]; 27 string s; 28 29 void read() 30 { 31 cin>>n>>t>>s; 32 } 33 34 long long cal_sec(int x,int y) 35 { 36 if(sec[x][y]!=-1) return sec[x][y]; 37 long long res=0; 38 for(int i=x;i<=y;i++) 39 { 40 res=res*10+s[i]-'0'; 41 } 42 return sec[x][y]=res; 43 } 44 45 void solve() 46 { 47 memset(sec,-1,sizeof(sec)); 48 memset(dp,-1,sizeof(dp)); 49 dp[0][0]=1; 50 for(int i=1;i<n;i++) 51 { 52 for(int j=1;j<=t && j<=i;j++) 53 { 54 for(int k=0;k<i;k++) 55 { 56 if(dp[k][j-1]!=-1) 57 { 58 dp[i][j]=max(dp[i][j],dp[k][j-1]*cal_sec(k,i-1)); 59 } 60 } 61 } 62 } 63 ans=0; 64 for(int i=0;i<n;i++) 65 { 66 if(dp[i][t]!=-1) ans=max(ans,dp[i][t]*cal_sec(i,n-1)); 67 } 68 } 69 70 void print() 71 { 72 cout<<ans<<endl; 73 } 74 75 int main() 76 { 77 read(); 78 solve(); 79 print(); 80 }