被水题卡了三个点,真是不开心...

题目链接:Doing Homework

思路:全排列枚举所有的状态,找出哪种状态时减少的人数最少,状态最多是2^15-1。不到40000,不会超时。然后为了节省空间,可以用状压DP,以一个16位的十进制数的第0位,第1位...第n-1为的0或1分别表示第1项,第2项和第n项任务未完成或已完成。因为要输出路径,所以保存每个的前驱,输出路径时,dp[i].pre^i = j 得到的j是从dp[i].pre状态完成j任务时到了i状态。

感觉状压DP就是普通的DP加上位运算,目前这么感觉。而且多用于暴力枚举?大概吧。

 

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <math.h>
#define maxn 1<<16
using namespace std;

struct Task {
    char name[210];
    int dead;
    int cost;
}task[210];

struct Dp {
    int pre;
    int cost;
    int resco;
}dp[maxn];

bool vis[maxn];

void out(int now) {
    if (dp[now].pre == 0) {
        int cnt = 0;
        while(now > 0) {
            cnt++;
            now /= 2;
        }
        cout << task[cnt-1].name << endl;
        return;
    }
    else if (dp[now].pre != 0) {
        int pre = dp[now].pre;
        int cur = (pre ^ now); // 得到的是由上一步到这一步完成的任务。
        out(pre);
        int cnt = 0;

        while(cur > 0) {
            cnt++;
            cur /= 2;
        }
        cout << task[cnt-1].name << endl; // 输出这步任务的名字
        return;
    }
}

int main() {
    int t;
    int n;
    cin >> t;
    while(t--) {
        cin >> n;
        for (int i=0; i<n; ++i) {
            cin >> task[i].name >> task[i].dead >> task[i].cost;
        }

        int upstu = (1<<n)-1; // 状态数上限
        memset(vis, 0, sizeof(vis));
        memset(dp, 0, sizeof(dp));
        // init
        dp[0].pre = -1;
        dp[0].cost = 0;
        dp[0].resco = 0;
        vis[0] = 1;

        int now, nxt;
        for (int stu=0; stu<upstu; ++stu) { // 遍历所有状态
            for (int work=0; work<n; ++work) { // 遍历当前状态下的所有任务
                now = (1<<work);

                if ((now & stu) == 0) { // 任务没做
                    nxt = (now | stu);

                    //计算当前状态下完成该任务需要的时间和会有的罚时
                    int tcost = dp[stu].cost + task[work].cost;
                    int tresco = 0;
                    if (tcost > task[work].dead) {
                        tresco = tcost - task[work].dead;
                    }

                    tresco += dp[stu].resco; // 这句不能加在两个if里,因为比较的扣分数就是这一段时间的所有扣分数。

                    if (!vis[nxt]) { // 如果这种状态没有出现过
                        dp[nxt].cost = tcost;
                        //dp[nxt].resco = (tresco + dp[stu].resco);
                        dp[nxt].resco = tresco;
                        dp[nxt].pre = stu;
                        vis[nxt] = 1;
                    }
                    else { // 如果出现过
                        if (dp[nxt].resco > tresco) { //选择扣分数较少的
                            //dp[nxt].resco = (tresco + dp[stu].resco);
                            dp[nxt].resco = tresco;
                            dp[nxt].cost = tcost;
                            dp[nxt].pre = stu;
                        }
                    }
                }
            }
        }
        cout << dp[upstu].resco << endl;
        out(upstu);
    }
    return 0;
}

  

posted on 2016-03-24 21:11  小小八  阅读(185)  评论(0编辑  收藏  举报