題目鏈接
題意
可以參見紫書P274
1. n首歌,在t-1秒內唱完
2. 唱完之後唱678s的金曲,這樣就能使唱的時間最長
3. 我們要先保證唱的總曲目最多,當曲目相同的時候保證總時長最長
解決
- 經典的01揹包問題
- 考慮到狀態比較複雜,有曲目,有時長.我們把狀態用一個結構體來表示
- 這裏參考了下博客,用了滾動數組
- 要注意一下,如果前n首的總時長<=t-1,那麼這n首歌都可以唱
- 注意一下我們最終的狀態是t-1的狀態而不是t的狀態
我也是算法新手,對於滾動數組我是這麼理解的
1. 我們遍歷每首歌在一些情況下的最優解(也就是最外層的for(i) )
2. 可以看一下紫書的滾動數組的講解,我能力還不足以講清
3. 我還是再想想吧^_^,我會補充的!
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int t[52];
struct node
{
int num,time;
bool operator>(const node &n2)const
{
if(num==n2.num) return time>n2.time;
return num>n2.num;
}
}dp[180*52];
int main()
{
int c=1,sum_t,cases;
scanf("%d",&cases);
while(cases--)
{
printf("Case %d: ",c++);
int n,max_t;
scanf("%d%d",&n,&max_t);
sum_t=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&t[i]);
sum_t+=t[i];
}
if(sum_t<=max_t-1)
{
printf("%d %d\n",n+1,sum_t+678);
continue;
}
max_t--; //選的歌曲總時長應<=max_t-1而不是max_t(這樣就沒法唱金曲了)
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)
{
for(int j=max_t;j>=t[i];j--) //滾動數組中j只能從後往前掃
{
node tmp;
tmp.num=dp[j-t[i]].num+1; //狀態轉移
tmp.time=dp[j-t[i]].time+t[i]; //狀態轉移
if(tmp>dp[j]) dp[j]=tmp; //類似於01揹包問題中,能放下的情況
}
}
printf("%d %d\n",dp[max_t].num+1,dp[max_t].time+678);
}
}