题目链接:[kuangbin带你飞]专题十二 基础DP1 D - Doing Homework
题意
有n门功课需要完成,每一门功课都有时间期限以及你完成所需要的时间,如果完成的时间超出时间期限多少单位,就会被减多少学分,问以怎样的功课完成顺序,会使减掉的学分最少,有多个解时,输出功课名排列最小的一个。
思路
利用二进制位的方法来解题用过不少次,但真正意义上的状态压缩dp,这是第一道,有纪念意义,当然,一开始也没想出来,看了前人思路才恍然大悟。
先大致说说状态压缩,假设有三门作业a,b,c
那么,abc都做完即111,111可由101,110,011任意一个来得到。而101可以从100或者001来得到,这就是状态压缩dp的一个基本的状态转移。
之前自己一直奇怪二进制10串怎么能跟全排列扯上关系,相通上面那么思路,当然就迎刃而解。
代码
#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<algorithm>
#include<stack>
#include<queue>
using namespace std;
const int N = 16;
struct Node
{
char str[109];
int want, need;
}node[N];
struct DP
{
int now, sum, next, pos;
}dp[1<<N];
void put_ans(int x)
{
if(dp[x].next != -1)
{
put_ans(dp[x].next);
printf("%s\n", node[dp[x].pos].str);
}
}
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
int n;
scanf("%d", &n);
for(int i=0; i<n; i++)
scanf("%s%d%d", node[i].str, &node[i].want, &node[i].need);
dp[0].now = dp[0].sum = 0;
dp[0].next = dp[0].pos = -1;
int m = (1<<n)-1;
for(int i=1; i<=m; i++)
{
dp[i].sum = 0x3f3f3f3f;
for(int j=0; j<n; j++)
{
if((1<<j) & i)
{
int k = i - (1<<j);
int v = dp[k].now + node[j].need - node[j].want;
v = max(v, 0);
if(dp[i].sum >= dp[k].sum+v)
{
dp[i].sum = dp[k].sum + v;
dp[i].now = dp[k].now + node[j].need;
dp[i].next = k;
dp[i].pos = j;
}
}
}
}
printf("%d\n", dp[m].sum);
put_ans(m);
}
return 0;
}