usaco P2736 “破锣摇滚”乐队 Raucous Rockers(搜索或类似01的DP)

P2736 “破锣摇滚”乐队 Raucous Rockers

    • 150通过
    • 350提交
  • 题目提供者该用户不存在
  • 标签 USACO云端
  • 难度 普及+/提高
  • 时空限制 1s / 128MB

最新讨论

  • 暂时没有讨论

题目描述

你刚刚继承了流行的“破锣摇滚”乐队录制的尚未发表的N(1 <= N <= 20)首歌的版权。你打算从中精选一些歌曲,发行M(1 <= M <= 20)张CD。每一张CD最多可以容纳T(1 <= T <= 20)分钟的音乐,一首歌不能分装在两张CD中。

不巧你是一位古典音乐迷,不懂如何判定这些歌的艺术价值。于是你决定根据以下标准进行选择:

1.歌曲必须按照创作的时间顺序在所有的CD盘上出现。(注:第i张盘的最后一首的创作时间要早于第i+1张盘的第一首)

2.选中的歌曲数目尽可能地多

输入输出格式

输入格式:

第一行: 三个整数:N, T, M.

第二行: N个整数,分别表示每首歌的长度,按创作时间顺序排列。

输出格式:

一个整数,表示可以装进M张CD盘的乐曲的最大数目。

输入输出样例

输入样例#1:
4 5 2
4 3 4 2
输出样例#1:
3

说明

题目翻译来自NOCOW。

USACO Training Section 3.4

代码

搜索写的


#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<cmath>
using namespace std;
int a[27],cd[27],f=1,n,m,t,ans=-1;		//a表示每首歌,cd为当前时间,f为使用的第几个CD
void dfs(int pos,int sum){
	if(sum+n+1-pos<=ans)return ;	//唯一剪枝,假设后面全可以收也不是最优时,就pass
									//没有这个1000ms,加了它2ms
	if(pos==n+1){					
		ans=max(ans,sum);
		return;
	}
	//一共就三种方案
	if(cd[f]>=a[pos]){			//当前cd直接装上
		cd[f]-=a[pos];
		dfs(pos+1,sum+1);
		cd[f]+=a[pos];
	}
	if(f<m && t>=a[pos]){		//换一个盘
		f++;
		cd[f]-=a[pos];
		dfs(pos+1,sum+1);
		cd[f]+=a[pos];
		f--;
	}
	dfs(pos+1,sum);				//直接跳过这个
	return ;
}

int main(){
#ifndef ONLINE_JUDGE
  freopen("input.in","r",stdin);
  freopen("output.out","w",stdout);
#endif
	int i,j,k;
	scanf("%d%d%d",&n,&t,&m);
	for(i=1;i<=m;i++)
		cd[i]=t;
	for(i=1;i<=n;i++)
		scanf("%d",&a[i]);
	dfs(1,0);
	printf("%d",ans);
	return 0;
}

DP写的
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
using namespace std;
int a[110],dp[111][111][111],ans;		//dp[i][j][k],i表示放第i首歌
										//j表示放入第j个CD,k表示CD剩下的时间
int main(){
#ifndef ONLINE_JUDGE
	freopen("input.in","r",stdin);
	freopen("output.out","w",stdout);
#endif
	int i,j,k,m,n,t; 
	scanf("%d%d%d",&n,&t,&m);
	for(i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for(i=1;i<=n;i++)
		for(j=m;j>=1;j--)				//当做01背包搞
			for(k=t;k>=a[i];k--){		//每首歌只能放一次,要倒着枚
				dp[i][j][k]=dp[i-1][j-1][t]+1;
				for(int u=0;u<i;u++)
					if(dp[u][j][k-a[i]]+1>dp[i][j][k])
						dp[i][j][k]=dp[u][j][k-a[i]]+1;		//把前面可以放入的·
			}
	for(i=1;i<=n;i++)
		if(ans<dp[i][m][t])
			ans=dp[i][m][t];
	printf("%d",ans);
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值