zoukankan      html  css  js  c++  java
  • HDU 1074 Doing Homework 状压DP

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

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

      

  • 相关阅读:
    【奇妙dp】ARC107D Number of Multisets
    【最短路-拆点】ARC061Cすぬけ君の地下鉄旅行/Snuke's Subway Trip
    【数学-思维-枚举方式】ARC060B 桁和/Digit Sum
    ARC107C Shuffle Permutation【有脑就行qwq/完全不知道怎么分类嘛】
    【kmp-循环节】ARC060D 最良表現/Best Representation
    【简单dp】ARC059C キャンディーとN人の子供 / Children and Candies
    【状压】ARC058E 和風いろはちゃん / Iroha and Haiku
    快速乘
    Miller Rabin素数测试和Pollard Rho算法
    JAVA补充-接口
  • 原文地址:https://www.cnblogs.com/icode-girl/p/5317181.html
Copyright © 2011-2022 走看看