题目在这里
这里就默认大家已经看过题目了(没看过题目谁会搜题解是吧)
题目有两个约束条件(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;
}