【题解】牛客寒假基础算法训练营5 H 炫酷雪花

题目在这里
这里就默认大家已经看过题目了(没看过题目谁会搜题解是吧)


题目有两个约束条件(1.要求跳起来的次数最少。2.在跳起来最少的前提下,如果有多种方案,输出字典序最小)

第一个很好求,我们贪心一下,将较大的减掉,直到a[i]的和小于等于k为止。我们就可以求出最小要跳几次。

至于第二个,求字典序最小,然后数据只有5000,所以这个题目一看就很dp,至于怎么dp呢,马上就为大家解答。

首先,由于我们要求的是字典序最小,我们就希望’0’较先出现,'0’表示不跳。所以,我们将整个过程分成三段:第一段(1~(i-1)),表示已经受过的寒冷程度sum。第二段(i),当前的这个时间的选择(跳 or 不跳)。第三段((i+1)~n),从i之后到结束,我们还需要跳ans次,跳ans次之后受到的最小寒冷度是多少。
那么我们设dp[i][j]表示从(i~n)跳j次受到的最小寒冷度,a[i]表示当前这个时间不跳受到的寒冷度,sum表示前段已经受到的寒冷度,ans表示我们还有ans次跳的机会。
那么,由于我们希望’0’出现在较早的位置,所以当我们能不跳就不跳。这个怎么判断呢?其判断方式如下:
sum+a[i]+dp[i+1][ans]<=k 表示i这个点不跳,从i+1到n还能跳ans次的最小值,如果其值<=k,那么就说明当前这个点可以不跳,于是sum += a[i]
sum+a[i]+dp[i+1][ans]>k 表示这个点不跳,后面取最小值还是超过k了,所以这个点我们不能不跳,于是ans-1,sum不变。

ans就是通过第一步的贪心,每次选最大的求得的。

看到这里,相信读者已经理解了思路了,也相信不少nb网友已经AC啦!

但是不放代码就是耍流氓啊!

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn = 5010;
ll dp[maxn][maxn];
ll n,k;
ll a[maxn];
struct node
{
	ll v;
	int id;
	bool operator<(const node &x)const
	{
		if(x.v==v) return id<x.id;
		return v<x.v;
	}
};
priority_queue<node> que;
int main()
{
	scanf("%lld%lld",&n,&k);
	ll sum = 0;
	for(int i=1;i<=n;i++) 
	{
		scanf("%lld",a+i);
		que.push({a[i],i});
		sum += a[i];
	}
	ll ans = 0;
	ll tmp = sum;
	ll cnt = n;
	while(tmp>k)
	{
		tmp -= que.top().v;
		que.pop();
		ans++;
		cnt--;
	}
	memset(dp,INF,sizeof(dp));
	dp[n][1] = 0;
	dp[n][0] = a[n];
	for(int i=0;i<maxn;i++) dp[n+1][i] = 0;
	for(int i=n-1;i;i--)
	{
		dp[i][0] = dp[i+1][0]+a[i];
		for(int j=1;j<=n;j++)
		{
			dp[i][j] = min(dp[i+1][j]+a[i],dp[i+1][j-1]);
		}
	}
	sum = 0;
	string str;
	for(int i=1;i<=n;i++)
	{
		if(sum+a[i]+dp[i+1][ans]<=k && ans>=0)
		{
			sum += a[i];
			str += "0";
		}
		else
		{
			ans--;
			str += "1";
		}
	}
	printf("%lld\n",cnt);
	cout<<str<<endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值