简介:
如果你问一个麦霸:你在KTV有什么必唱曲目吗?得到的答案一般都会包含一首《劲歌金曲》
为什么呢?一般来说,KTV是不会时间到的时候鲁莽地把正在唱的歌切掉,而是会等到ta放完
所以为了占一点小便宜,最后一首歌一般都是《劲歌金曲》,因为ta有11分18秒长!!!
现在给出n首歌以及唱歌时间,要求在唱的曲目尽量多的情况下(《劲歌金曲》必唱),离开的时间尽量晚
分析:
又是一次题目数据范围瞎胡扯。。。
注意到t<=1e9,一般的算法好像都GG了
然而题目有一个很奇怪的限制:Each length will be less than 3 minute
一共50首歌,每首歌最多180秒,再加上劲歌金曲的678秒
总时长不超过180*50+678
所以这道题可以用n^2的算法
于是我们就选择了01背包
但是这道题有一些不同的地方:
- 首先我们必唱《劲歌金曲》,但是我们不会傻到在“正常时间”里大唱特唱,ta是用来拖时间的
所以说我们需要预留出一秒来开始《劲歌金曲》 - 我们不能放着不唱不是吗,所以在转移的时候,一定要从之前有歌的状态转移过来
if (j==t[i]||f[j-t[i]]>=1) //唱这首歌之前,前面不能不唱啊
f[j]=max(f[j],f[j-t[i]]+1);
- 我们要在最多曲目的基础上待最长时间,所以我们用朴素背包求出最多曲目之后
从后往前扫一遍,找到第一个f[i]==ans的i,最后答案就是i+678 - 注意ans=0的情况:如果我们一首歌都唱不完,那我们直接唱《劲歌金曲》,答案就是678s
tip
看到数据范围千万别慌,一定要仔细考虑,毕竟出题人都不什么清真之人
再简单的题,再基础的题,也有需要注意的细节
//这里写代码片
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int n,tim;
int t[51];
int f[180*50+700];
void doit()
{
int i,j;
memset(f,0,sizeof(f));
int ans=0;
for (i=1;i<=n;i++)
for (j=tim-1;j>=t[i];j--) //留最后一秒开始唱金歌劲曲
if (j==t[i]||f[j-t[i]]>=1) //唱这首歌之前,前面不能不唱啊
{
f[j]=max(f[j],f[j-t[i]]+1);
ans=max(ans,f[j]);
}
printf("%d",ans+1);
if (ans==0)
{
printf(" 678\n");
return;
}
for (i=tim-1;i>=1;i--)
if (f[i]==ans)
{
printf(" %d\n",i+678);
break;
}
return;
}
int main()
{
int T;
scanf("%d",&T);
for (int cas=1;cas<=T;cas++)
{
scanf("%d%d",&n,&tim);
for (int i=1;i<=n;i++) scanf("%d",&t[i]);
printf("Case %d: ",cas);
doit();
}
return 0;
}