hdu1074 状压DP、栈实现记录路径

题意:给了几门学科作业、它们的截止提交期限(天数)、它们的需要完成的时间(天数),每项作业在截止日期后每拖延一天扣一学分,算最少扣的学分和其完成顺序。

一开始做的时候,只是听说过状态压缩这个神奇的东西,但事实上我并不会用它,所以白白想了一个晚上没想出来,然后就看了一下题解```再见吧朋友又是新的算法要学了。

状态压缩,实际上就是用二进制的方式,对于每一个要考察的状态用0/1表示其完成与否,这样当从 1 遍历到 111```111 的时候,就可以遍历完所有的状态了,在遍历的过程中利用位运算以及状态转移就能够最终实现DP。

首先对于每一个状态 i 初始化其罚分为最大值用于最开始的比较,用 & 运算来判断它的其中某项作业 j 是否已经完成,完成则 & 运算结果为 1 ,否则为 0 ,若已完成,那么考察 j 作业未完成的情况 last ,如果从 last 状态的罚分加上完成作业 j 后的罚分小于当前 i 状态的罚分,则更新 i 状态的罚分情况,并且记录下到 i 状态时做的作业 j 、 i 的上一状态 last 、i 状态的已花费时间,便于进行状态转移和记录路径。最后用栈从全部作业完成时 111`````111 的状态开始通过记录的上一状态往前递推,输出作业次序。

收获颇丰,但是心力交瘁```代码中间绿绿的是调试的时候用的```因为我蠢```

#include<stdio.h>
#include<string.h>
#include<stack>
#include<algorithm>
#define INF 1<<30
using namespace std;
struct Sub{
    char name[100];
    int d,t;
}S[16];

struct dpl{
    int t,s,p,n;
}dp[1<<16];

int main(){
    int T;
    while(scanf("%d",&T)!=EOF){
        int N;
        for(int q=1;q<=T;q++){
            scanf("%d",&N);
            memset(dp,0,sizeof(dp));
            int i,j;
            for(i=1;i<=N;i++){
                scanf("%s%d%d",S[i].name,&S[i].d,&S[i].t);
            }
    /*        for(i=1;i<=N;i++){

                printf("%s %d %d",S[i].name,S[i].d,S[i].t);
            }*/
            int sum=(1<<N)-1,sub,last,pun;
    /*        printf("%d ",sum);
            while(sum){
                printf("%d",sum&1);
                sum>>=1;
            }
            printf("\n");*/
            for(i=1;i<=sum;i++){
                dp[i].s=INF;
                for(j=N;j>0;j--){
                    sub=1<<(j-1);
                    if(i&sub){
                        last=i-sub;
                        pun=dp[last].t+S[j].t-S[j].d;
                        if(pun<0)pun=0;
                        if(pun+dp[last].s<dp[i].s){
                            dp[i].s=pun+dp[last].s;
                            dp[i].t=dp[last].t+S[j].t;
                            dp[i].p=last;
                            dp[i].n=j;
                        }
                    }
                }
            }
            stack<int>s;
            int t=sum;
            printf("%d\n",dp[t].s);
            while(t){
                s.push(dp[t].n);
                t=dp[t].p;
            }
            while(!s.empty()){
                printf("%s\n",S[s.top()].name);
                s.pop();
            }
        }
    }
    return 0;
}
时间: 2025-01-07 10:42:08

hdu1074 状压DP、栈实现记录路径的相关文章

P1171 售货员的难题 - 状压DP【最短Hamilton路径】

P1171 售货员的难题 Sol: 最短Hamilton路径,经典的NPC问题,小数据可以通过状压DP 实现. 状态:\(f[i][j]\)表示当前在第i号点,且已经过的点的状态为j 时的最短Hamilton路径. 阶段:若以点为阶段,由于会从点i转移到点i+1,还可能从i+1转移到i-1,不具有无后效性,因此我们考虑以二进制状态为阶段进行转移. 决策:考虑由哪一个点转移而来. 转移:\(f[i][j]=\max \limits_{i\&(1<<i)\&\&i\&

HDU 4539郑厂长系列故事――排兵布阵(状压DP)

HDU 4539  郑厂长系列故事――排兵布阵 基础的状压DP,首先记录先每一行可取的所哟状态(一行里互不冲突的大概160个状态), 直接套了一个4重循环居然没超时我就呵呵了 1 //#pragma comment(linker,"/STACK:102400000,102400000") 2 #include <map> 3 #include <set> 4 #include <stack> 5 #include <queue> 6 #i

Codeforces 453B Little Pony and Harmony Chest:状压dp【记录转移路径】

题目链接:http://codeforces.com/problemset/problem/453/B 题意: 给你一个长度为n的数列a,让你构造一个长度为n的数列b. 在保证b中任意两数gcd都为1的情况下,使得 ∑|a[i]-b[i]|最小. 让你输出构造的数列b. (1<=n<=100, 1<=a[i]<=30) 题解: 因为1<=a[i]<=30,所以有1<=b[i]<=60,此时才有可能最优. 因为b中任意两数gcd为1,所以对于一个质因子p[i]

HDU1074:Doing Homework(状压DP)

Doing Homework Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 15868    Accepted Submission(s): 7718 Problem Description Ignatius has just come back school from the 30th ACM/ICPC. Now he has a l

HDU 1074:Doing Homework(状压DP)

http://acm.hdu.edu.cn/showproblem.php?pid=1074 Doing Homework Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 7704    Accepted Submission(s): 3484 Problem Description Ignatius has just come bac

HDU 1074 Doing Homework DP 状压DP

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1074 题目描述: 给你所有课程的截止时间和工作时长, 一次只能做一种作业, 问最少罚时几天 N <= 15 解题思路: 由于N很小, 所以第一反应就是状压DP, 我们可以用一个15位二进制数来表示各个课程做完还是没做完, 然后从 S 从 1 到 1 << N 枚举 i 从 1 到 N 枚举, 如果S & (1<<i) 有效则说明i 属于情况 S, 这样我们从上一步S -

状压DP [HDU 1074] Doing Homework

Doing Homework Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 5472    Accepted Submission(s): 2311 Problem Description Ignatius has just come back school from the 30th ACM/ICPC. Now he has a lo

CF11D A Simple Task(状压DP)

\(solution:\) 思路大家应该都懂: 状压DP:\(f[i][j]\),其中 \(i\) 这一维是需要状压的,用来记录19个节点每一个是否已经走过(走过为 \(1\) ,没走为 \(0\) ,用 \(2\)进制 压缩一下即可).同时,我们认为状压中已经走过的序号最小的节点为出发节点,\(j\) 即数组第二维是路径终点.(当这两个数相同时,说明找到了一个环). 注:这种方法因为无向图的存在,会出现(同一条路径出现两次)(一条边和两个端点构成非法环)的情况,这只需要在输出答案时 \(ans

状压dp,区间dp,矩阵快速幂

DP 首先先回忆一下dp,dp叫做记忆化搜索,是一种可以把暴力搜索中重复的部分重复利用,从而到达减小复杂度的目的.比如最应该熟悉的背包模型,如果你把选择的过程看成一步一步的,那么在这么多的搜索路径中一定有着很多很多的重复部分,dp就是一种把重复的部分加以利用的方法.相信大家都已经在以前的练习中已经明白了dp是什么样的思路了,接下来的两种dp会在大家已经了解经典的背包dp等模型下展开. 状态压缩dp: 首先先讲一个状压dp最最经典的模型,求哈密尔顿路径问题,也叫做旅行商问题:给你一张图,你要在每个