程序设计思维与实践 Week12 E状压DP

题意:

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

input:

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

output:

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

样例:

输入:
2
3
Computer 3 3
English 20 1
Math 3 2
3
Computer 3 3
English 6 3
Math 6 3

输出:
2
Computer
Math
English
3
Computer
English
Math

思路:

状压DP实在是有点不大能想到,跟着PPT上代码思路和同学们的一些解释写的,能看懂,但自己的话肯定想不到,也不会用。主要让自己熟悉一下这个题型吧。
首先f[S],其中S为任务集合,sum[i],为每个集合的对应的总时间,pre[]为前序,即在S的时候记录加入的是哪一个任务。通过枚举每一个任务集合,即S,然后对这个S集合,枚举每一个任务,看加入当前任务的时候,其总罚时的情况,如果更小,则更新f[S],sum[S]和pre[S]。其主要的转移方程都是课上的PPT上的。最终输出最后的发f[max],即可得到答案。然后根据pre[],依次找到输出的任务即可。有一个可以学习的,就是要求的字典序最小输出,可以在一开始的时候就对各个任务进行排序,之后得到的便是字典序最小的。

代码:

#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <algorithm>
using namespace std;

const int inf=1e8;
struct work{
	int d,c;
	string Name;
	bool operator<(const work &p)const{
		if(Name!=p.Name) return Name<p.Name;
	}	
}W[20];
int m,n;
int f[1<<16], sum[1<<16],pre[1<<16]; //记录罚时  作业集合对应的总时间 

void output(int x){
	if(x==0) return;
	output(x-(1<<pre[x]));
	cout<<W[pre[x]].Name<<endl;
}


int main(){
	ios::sync_with_stdio(0);
	cin>>m;
	for(int i=0; i<m;i++){
		cin>>n;
		memset(f,0,sizeof(f));
		memset(sum,0,sizeof(sum));
		for(int j=0;j<n;j++){
			cin>>W[j].Name>>W[j].d>>W[j].c;
		}
		sort(W,W+n);	
		int now=(1<<n)-1; //记录当前最大的集合 
		
		int tmp;
		for(int S=1;S<=now; S++){  //枚举每一个状态 
			f[S]=inf;	
			for(int x=0;x<n;x++){  //枚举每一个作业 
				if(!(S&(1<<x))) continue;  //没有x的点 
				tmp=max(sum[S-(1<<x)]+W[x].c-W[x].d,0);  //加入这个点的总罚时 
				if(f[S]>=f[S-(1<<x)]+tmp){
					f[S]=f[S-(1<<x)]+tmp;
					sum[S]=sum[S-(1<<x)]+W[x].c;
					pre[S]=x;  //在S时加入了第x任务 
				} 	
			}	
		}
		cout<<f[now]<<endl;
		output(now);	
	}
	return 0;
} 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值