课程_大_作业

(内心BB:
写的时候要死,写完之后一顿通畅(其实是看到了表示救赎的蓝色…)
这个用到状态压缩的方式,因为最多只有15组数据,所以枚举选或不选的状态可以用二进制表示。
观摩了大佬的代码之后,觉得自己真是弱爆了,原来还可以这样子写,为什么我就是想不出来呢,原地去世。。。)
我是参考的这个博客的代码,链接: link

分析思路:
注意的点有:

· 根据字典序进行选择输出。

· 状态的第i位表示有没有写这门课的作业,1表示前面已经写了,0表示还没有。

· 然后主要是对每一个状态进行一次最后做什么作业会使结果更好的最优选择。也可以说是状态转移方程叭:dp[i] = dp[i ^ (1 << j)] + cha(sum[i], d[j]);

· 要进行判断,假如把这个放在最后写的话dp[i]的更优秀的方法,然后就将这个方案状态存入ans[i]里面,每个状态都会最后存的都会是(这个已经做了的作业组合中)罚时最少的。

(注意当找到该状态下不同的最后一个做的作业但是罚时相同的时候,就要进行字典序判断的操作了)

代码
(防止自己到时候看不懂写了一堆注释hhhhhhh…)

#include<iostream>
#include<cstring>
#include<algorithm>
//要用到状态压缩的方法......还要考虑输出字典序小的那种方案
using namespace std;
const int MAX = 1 << 16;
int dp[MAX];//用来表示把每一个状态中课程全部完成的最少  罚时
int t[20];//表示每一门课程的完成的时间
int d[20];//课程的ddl
int sum[MAX];//用来表示某个状态的完成时间的总和
string name[20];
string ans[MAX];

int cha(int a, int b)
{
	return max(0, a - b);//如果a大于b就返回两个的差,否则返回0
}
//注意用二进制来表示,每个课有选和不选,罚分多少和选课的顺序无关,和现在已经选了什么课的总时间有关(我是这么想的)

int main()
{
	int T;
	cin >> T;
	while (T--)
	{
		int n;
		cin >> n;
		for (int i = 0; i < n; i++)
		{
			cin >> name[i] >> d[i] >> t[i];
			sum[1 << i] = t[i];//只完成了i的课的需要的时间
		}
		for (int i = 1; i < (1 << n); i++)//枚举所有的状态的情况,注意每个状态都是不一样,共有2^n种情况(选与不选,枚举一下)
		{
			for (int j = 0; j < n; j++)
			{
				//如果这个情况已经选了这个j的课,那么就计算一下现在这个状态的
				if (i & (1 << j)) sum[i] = sum[i ^ (1 << j)] + t[j]; 
				//时间的计算就是现在的情况下除去j课的总时间sum[i^(1 << j)]加上j的时间(人人为我)
			}
		}
		dp[0] = 0;//初始状态
		for (int i = 0; i < (1 << n); i++)
		{
			ans[i] = "";//进行初始化
		}
		for (int i = 1; i < (1 << n); i++)
		{
			for (int j = 0; j < n; j++)
			{
				//对现在的状态i已经有的所有的课都进行一遍筛选
				//如果这个状态是选有j课的,如果这个状态的情况还没有进行过ans的记录那就一定要走一趟了 或者如果将j放在最后写的话的这个情况的罚时是小于等于现在的状态的罚时的话,就需要进行替换,或者进行字典排序了
				if (i & (1 << j) && (dp[i ^ (1 << j)] + cha(sum[i], d[j]) <= dp[i] || ans[i] == ""))
				{
					if (dp[i ^ (1 << j)] + cha(sum[i], d[j]) < dp[i] || ans[i ^ (1 << j)] + name[j] + "\n" < ans[i] || ans[i] == "")
					{//如果这种j最后做的情况的罚时是明确小于目前的情况的,或者是等于的时候发现j最后做的情况的字典排序更小(带回车进行比较的),那就选最优解
						ans[i] = ans[i ^ (1 << j)] + name[j] + "\n";
					}
					dp[i] = dp[i ^ (1 << j)] + cha(sum[i], d[j]);//更新最小罚时!!
				}
			}
		}
		cout << dp[(1 << n) - 1] << endl;
		cout << ans[(1 << n) - 1];//已经包括有回车了(每次加一个课程都考虑了回车的问题)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值