虽然可以算得上是道水题,但作为我第一道状压DP,还是把它写入我的博客。
觉得这道题作为入门题是极好的,比网上说的什么矩阵的好理解的多!!
因为几乎只涉及了状压
特别想感慨一句,dp真的是个优美的暴力啊……
题意:
有几门课的作业,分别给出截止时间,完成所需时间,没拖一天扣一分,问最少扣多少分,并输出写作业顺序。
思路:
总的思路是对于每一个状态,都进行课程的枚举,并计算花费时间,拖作业时间,从而进行状态转移,每次状态转移都使拖作业的时间最小即可。
不是暴力??但是真的很优美!
课程数很小,只有15门,因此状压无压力。
AC Code
#include <cstdio>
#include <cstring>
#include <numeric>
#include <queue>
#include <stack>
using namespace std;
#define each(i, n) for (int(i) = 0; (i) < (n); (i)++)
#define reach(i, n) for (int(i) = n - 1; (i) >= 0; (i)--)
#define range(i, st, en) for (int(i) = (st); (i) <= (en); (i)++)
#define rrange(i, st, en) for (int(i) = (en); (i) >= (st); (i)--)
#define fill(ary, num) memset((ary), (num), sizeof(ary))
const int max_n = 16;
const int max_m = 110;
int n;
bool vis[1<<max_n];
struct co {
int deadline, cost;
char name[max_m];
} course[max_n];
struct p {
int cost, bey, pre;
} dp[1 << max_n];
void output(int state)
{
if (!state)
return;
output(dp[state].pre);
int idx = state ^ dp[state].pre, pos = 0;
while ((idx & 1) == 0)
idx >>= 1, pos++;
puts(course[pos].name);
}
int main()
{
int T;
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
fill(vis, false);
each(i, n) scanf("%s %d %d", course[i].name, &course[i].deadline, &course[i].cost);
int en = (1 << n);
each(i, en) each(j, n)
{
int j2 = (1 << j);
if (!(i & j2)) {
int cur = i | j2;
dp[cur].cost = dp[i].cost + course[j].cost;
int bey = (dp[cur].cost > course[j].deadline ? dp[cur].cost - course[j].deadline : 0) + dp[i].bey;
if (!vis[cur] || bey < dp[cur].bey)
vis[cur] = true, dp[cur].bey = bey, dp[cur].pre = i;
}
}
printf("%d\n", dp[en - 1].bey);
output(en - 1);
}
return 0;
}