HDU 1074 Doing Homework(状态压缩dp)

题目链接: [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;
}