Leetcode 312.戳气球

文章提供了三种方法解决戳破带有数字的气球问题,以获取最大数量的硬币。方法一是暴力深度搜索,时间复杂度高;方法二是动态规划,通过状态转移方程优化计算;方法三是尝试贪心策略但未成功。示例展示了如何应用这两种方法并给出了对应的C++代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

有 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;
}

方法三:其实想过贪心,不过似乎不行啊

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值