有 n
个气球,编号为0
到 n - 1
,每个气球上都标有一个数字,这些数字存在数组 nums
中。
现在要求你戳破所有的气球。戳破第 i
个气球,你可以获得 nums[i - 1] * nums[i] * nums[i + 1]
枚硬币。 这里的 i - 1
和 i + 1
代表和 i
相邻的两个气球的序号。如果 i - 1
或 i + 1
超出了数组的边界,那么就当它是一个数字为 1
的气球。
求所能获得硬币的最大数量。
示例 1:
输入:nums = [3,1,5,8] 输出:167 解释: nums = [3,1,5,8] --> [3,5,8] --> [3,8] --> [8] --> [] coins = 3*1*5 + 3*5*8 + 1*3*8 + 1*8*1 = 167
示例 2:
输入:nums = [1,5] 输出:10
提示:
n == nums.length
1 <= n <= 300
0 <= nums[i] <= 100
方法一
暴力深搜,每次从没有戳的气球发展状态,时间复杂度为O(n!)。
DFS(m,k),每个状态有两个参数,m表示还剩m个气球,k表示这次戳第k个气球。
此外还需一个bool数组t打标记,t[i]=0,表示第i个气球还未戳破。
首先需要找到第k个气球左边还未戳破且离第k个最近(其实就是与第k个实际相邻的),同理右边也需找到,这两个气球分别记为第l、r个。
那此次戳破气球获得的金币就为w=num[l]*num[k]*num[r]。(根据题意我们可以将num[0]和num[n+1]均赋值为1)
然后从1到n遍历,找到还未戳破的气球(即t[i]==0的),sum=max(sum,w+DFS(m-1,i)),sum表示将第k个和剩余m-1个气球戳破的最大金币数。
附上代码:
#include<cstdio>
#include<algorithm>
using namespace std;
int n,num[305];
bool t[305];//0表示未戳,1表示已戳
int DFS(int m,int k)//m表示剩余m个,k表示这次戳第k个
{
int l=k-1,r=k+1,sum=0,w;
if(m==0)
return 0;
t[k]=1;//标记第k个已戳
while(t[l])//找到左边离k最近的未戳破气球
l--;
while(t[r])//同理
r++;
w=num[l]*num[k]*num[r];
sum=w;
for(int i=1;i<=n;i++)
{
if(t[i]==0)
sum=max(sum,w+DFS(m-1,i));
}
t[k]=0;//递归返回此层时需将t[k]重置为0
return sum;
}
int main()
{
int ans=0;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&num[i]);
num[0]=1;//将num[0]、num[n+1]赋值为1,避免特判
num[n+1]=1;
for(int i=1;i<=n;i++)
ans=max(ans,DFS(n,i));//枚举戳的第一个气球
printf("%d",ans);
return 0;
}
方法二
动态规划,dp[i][j]表示戳破第i个到第j个气球所得的最大金币数。
状态转移方程:dp[i][j]=max(dp[i][k-1]+dp[k+1][j]+num[i-1]*num[k]*num[j+1]),k表示最后戳的气球。
至于为什么枚举最后一个戳破的气球,而不是第一个。以dp[i][k-1]为例,它代表戳破i~k-1个气球,且其左边与i-1相邻,右边与k相邻时的最大金币数。若k表示第一个戳破的气球,则i~k-1个气球右边相邻不再是第k个,而是k+1~j中的某一个。
时间复杂度大概是O(n^3)(其实不太会算啦)。
附上代码:
#include<cstdio>
#include<algorithm>
using namespace std;
int dp[1005][1005];//dp[i][j]表示戳破第i~j个气球的最大金币数
int main()
{
int n,num[1005];
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&num[i]);
num[0]=1;
num[n+1]=1;
for(int i=1;i<=n;i++)
dp[i][i]=num[i-1]*num[i]*num[i+1];//初始化dp[i][i]
for(int l=1;l<=n;l++)
{
for(int i=1;i+l<=n;i++)
{
for(int j=i;j<=i+l;j++)//枚举最后戳破的气球
dp[i][i+l]=max(dp[i][i+l],dp[i][j-1]+dp[j+1][i+l]+num[i-1]*num[j]*num[i+l+1]);
}
}
printf("%d",dp[1][n]);
return 0;
}
方法三:其实想过贪心,不过似乎不行啊