选做题-2 Week12作业E题

题目:

马上假期就要结束了,zjm还有 n 个作业,完成某个作业需要一定的时间,而且每个作业有一个截止时间,若超过截止时间,一天就要扣一分。
zjm想知道如何安排做作业,使得扣的分数最少。
Tips: 如果开始做某个作业,就必须把这个作业做完了,才能做下一个作业。

输入:
有多组测试数据。第一行一个整数表示测试数据的组数
第一行一个整数 n(1<=n<=15)
接下来n行,每行一个字符串(长度不超过100) S 表示任务的名称和两个整数 D 和 C,分别表示任务的截止时间和完成任务需要的天数。
这 n 个任务是按照字符串的字典序从小到大给出。

输出:
每组测试数据,输出最少扣的分数,并输出完成作业的方案,如果有多个方案,输出字典序最小的一个。

样例:
Input:

2
3
Computer 3 3
English 20 1
Math 3 2
3
Computer 3 3
English 6 3
Math 6 3

Output:

2
Computer
Math
English
3
Computer
English
Math

在第二个样例中,按照 Computer->English->Math 和 Computer->Math->English 的顺序完成作业,所扣的分数都是 3,由于 English 的字典序比 Math 小,故输出前一种方案。

思路:

本题使用状态dp,设f表示当前状态最少扣的分数。
采用枚举方法,枚举所有的作业,若某作业包含在当前情况中,则计算出当前作业被扣除的分数,更新当前状态的最小值,同时记录前驱,最后递归输出。由于需要字典序最小,因此逆序枚举作业,从而避免了排序。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxx=1e9;
int n,f[1<<16],pre[1<<16],sum[1<<16];
struct node
{
	string cla;
	int d;
	int c;
}a[16];
void output(int x)
{
	if(x==0) return;
	output(x-(1<<pre[x]));
	cout<<a[pre[x]].cla<<endl;
}
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		memset(f,0,sizeof(f));
		memset(pre,0,sizeof(pre));
		memset(sum,0,sizeof(sum));
		cin>>n;
		for(int i=0;i<n;i++)
		cin>>a[i].cla>>a[i].d>>a[i].c;
		int tot=(1<<n);
		for(int i=1;i<=tot;i++)
		{
			f[i]=maxx;
			for(int j=n-1;j>=0;j--)
			{
				int now=1<<j;
				if(!(i&now)) continue;
				int tmp=sum[i-now]+a[j].c-a[j].d;
				tmp=max(0,tmp);
				if(f[i]>f[i-now]+tmp)
				{
                    f[i]=f[i-now]+tmp;
                    sum[i]=sum[i-now]+a[j].c;
                    pre[i]=j;
                }
			}
		}
		cout<<f[tot-1]<<endl;
		output(tot-1);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值