洛谷U14667 肝活动【比赛】 【状压dp】

题目描述

Yume 最近在玩一个名为《LoveLive! School idol festival》的音乐游戏。他之所以喜欢上这个游戏,是因为这个游戏对非洲人十分友好,即便你脸黑到抽不出好卡,还可以通过在每个月举办的两次活动中达成一定的目标来获得奖励。

Yume 很喜欢这一期活动奖励卡的卡面,于是他决定要肝这一期的活动,拿到活动奖励。这一期的活动规则很特殊,玩家需要在活动规定的结束时间前,完成所有指定的歌曲(每首歌曲只能打一次),并获得一定的分数,就可以拿到活动奖励。如果在规定的时间前没有完成所有的歌曲,或者分数不够奖励的分数线,则不能领取活动奖励。每首歌有一个限定的奖励开放时间,玩家如果在这段时间内完成了这首歌,便可以获得一定的分数(获得的分数 = 开放时间 - 当前已用的总时间)。如果超出了这段时间之后再完成这首歌,就不能获得分数了。

这样的规则对 Yume 这样的老玩家来说本应是轻而易举,但不巧的是 Yume 把活动的结束时间记成了活动的开始时间,以至于当他上线跃跃欲试的时候,惊恐地发现活动已经快要结束了。现在他想知道,在剩余的时间之内,他能否完成所有的歌、达成奖励的分数线拿到活动卡。为了节省时间,他把这个问题交给了你来解决。请你根据给定的数据,帮他计算出能否在剩余的时间内达成目标。如果能,请告诉他完成每首歌曲的顺序。

输入输出格式

输入格式:

输入的第一行是三个整数 n, m, t,分别表示规定完成的歌曲数目、获得奖励需要达到的最低分数和距离活动结束剩余的时间。

接下来有 n 行,第 i 行有一个字符串 Si 和两个整数 Ti 和 Mi,表示第 i 首歌的歌名为 Si,完成第 i 首歌所需要的时间为 Ti,第 i 首歌的奖励开放时间剩余 Mi。保证 Ti ≤ Mi. 其中数据已按 Si 的字典序给出。

输出格式:

如果在活动结束前 Yume 可以完成指定的目标拿到奖励,则在第一行输出一个整数 C,表示在获得奖励的前提下,所能够获得的分数的最大值;接下来的 n 行中,按照完成歌曲的顺序输出第 i 首歌的歌名。如果有多种可能性,则输出字典序最小的情况。

如果在活动结束前 Yume 不能完成所有的歌曲,输出 No Answer .

输入输出样例

输入样例#1: 复制
3 2 10
BokutachiwaHitotsunoHikari 3 8
Korekara 1 2
SnowHalation 2 5
输出样例#1: 复制
6
SnowHalation
BokutachiwaHitotsunoHikari
Korekara
输入样例#2: 复制
2 1 2
AoizoraJumpingHeart 1 2
TimeLapse 2 4
输出样例#2: 复制
No Answer

说明

对于 0% 的数据,与测试数据完全相同。

对于 20% 的数据,满足 n ≤ 5。

对于 40% 的数据,满足 n ≤ 9。

对于 70% 的数据,满足 n ≤ 15。

对于 100% 的数据,满足 n ≤ 22,Si 的长度不超过 50. 保证 m, t 和 Ti, Mi 以及其相加的结果都在 int 的最大范围内。

另有 10% 的数据满足 Sigma(T1, T2, …, Tn) < t.



题解

码到这道题的时候还剩40min。。。
呜哇~这一堆堆的限制条件是什么鬼啊QAQ
无奈之中,往下一翻,n<=22 ( ·  · )
莫不是状压dp ???

静下心来回头读题,主人公强迫症还是咋样,非要肝完所有的曲,还要拿奖
也就是说在 肝完所有的曲的前提下 拿尽量多的分

曲子超时是可以继续肝的,但是没有分罢了,所以我们只需统计一下T[i]和总时间t比较一下就能判断了

重点在于怎么拿尽量多的分, 既然说了状压就试试呗:
设二进制状态1表示已经肝了的曲子,总状态有2^22 = 4194304个,已经很大了,我们就设一个1维的状态

设f[s]表示s状态下的最大分数,对于该状态下未肝过的曲子k,若转移成的状态为e
f[e] = max(f[e],f[s] + v)   【v表示肝完这首曲子有多少分,即M[i] - T[i] - t[s],若时间不够就是0】
t[s]表示s状态下已经用了多少时间。怎么求?同时再进行一次状态转移呗
t[e] = t[s] + T[i]

最后f[maxv]就是答案【maxv = (1<<n) - 1】
若f[maxv] < m,就拿不到奖,No Answer

抢着比赛最后10min肝完了。。。交上去才30分。。。。【后来通知是数据出了点问题,后来交就A了QAQ,啊不要紧了】

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long int
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define fo(i,x,y) for (int i = (x); i <= (y); i++)
#define Redge(u) for (int k = head[u]; k != -1; k = edge[k].next)
using namespace std;
const int maxn = 25,maxs = 55,maxm = 1 << 22,INF = 1000000000;

inline int read(){
	int out = 0,flag = 1;char c = getchar();
	while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
	while (c >= 48 && c <= 57) {out = out * 10 + c - 48; c = getchar();}
	return out * flag;
}

int f[maxm],tim[maxm],T[maxn],M[maxn],n,m,t,chs[maxm],pre[maxm];
char names[maxn][maxs];

void print(int s){
	if (!s) return;
	print(pre[s]);
	printf("%s\n",names[chs[s]]);
}

int main()
{
	int tot = 0;
	n = read(); m = read(); t = read();
	REP(i,n){
		scanf("%s",names[i]);
		tot += (T[i] = read());
		M[i] = read();
	}
	if (tot > t) {
		cout<<"No Answer"<<endl;
		return 0;
	}
	int maxv = (1 << n) - 1;
	for (int s = 0; s <= maxv; s++) f[s] = -INF;
	f[0] = 0;
	for (int s = 0; s <= maxv; s++){
		for (int i = 1; i <= n; i++){
			int e = 1 << i - 1,v = M[i] - T[i] - tim[s];
			if (v < 0) v = 0;
			if ((s | e) != s && f[e | s] < f[s] + v){
				f[s | e] = f[s] + v;
				tim[s | e] = tim[s] + T[i];
				pre[s | e] = s;
				chs[s | e] = i;
			}
		}
	}
	if (f[maxv] < m) cout<<"No Answer"<<endl;
	else {
		printf("%d\n",f[maxv]);
		print(maxv);
	}
	return 0;
}




转载于:https://www.cnblogs.com/Mychael/p/8282843.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值