dp训练计划——hdu1074状压dp入门题

题目链接:https://vjudge.net/problem/HDU-1074

题目大意:

给你n个作业的名字,截止时间,完成需要的时间,数据按照字典序大小给你

让你求出完成这个n个作业的最小延误时间(即超时时间),并输出字典序最小的安排顺序。

题解:

经典状压dp入门题,其实就是暴力枚举 ,只不过压缩一下用一个数表示全部的状态,然后暴力枚举所有状态,维护答案。

我们首先需要发现一个关键点:n的范围不大,最大只有15,那么我们就可以考虑枚举所有作业的状态,

即用一个长度为15位的2进制数表示,为0表示该作业未完成,为1表示该作业已经完成。这样我们就可以用0~(1<<15)-1来表示完成作业情况的所有状态。

然后我们来考虑状态,定义dp[i]表示状态为i时的最小延误时间,不难得到状态转移方程

dp[i]=min(dp[j]+从j状态转移到i状态的延误时间,dp[i]),这里j必须要是i的子状态,即i为111时,j必须要是110,011,101这几种状态,不难发现这个子状态就是i-(1<<j)(需要保证i&(1<<j)==1),而且我们需要倒着枚举,因为要保证字典序最小,

这样才能保证倒着枚举时,后面的能被前面的更新到,因为前面的字典序小。

扫描二维码关注公众号,回复: 9917594 查看本文章

记录路径输出时可以用栈,也可以直接利用递归来输出,本质也是栈。

代码实现 

#pragma GCC optimize(2)
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<queue>
#define PI atan(1.0)*4
#define E 2.718281828
#define rp(i,s,t) for (register int i = (s); i <= (t); i++)
#define RP(i,t,s) for (register int i = (t); i >= (s); i--)
#define ll long long
#define ull unsigned long long
#define mst(a,b) memset(a,b,sizeof(a))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define pii pair<int,int>
#define mp make_pair
#define pb push_back
#define debug printf("ac\n");
using namespace std;
inline int read()
{
    int a=0,b=1;
    char c=getchar();
    while(c<'0'||c>'9')
    {
        if(c=='-')
            b=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9')
    {
        a=(a<<3)+(a<<1)+c-'0';
        c=getchar();
    }
    return a*b;
}
const int INF = 0x3f3f3f3f;
const int N = 1e5+7;
struct node{
    string s;
    int d,c;
    bool operator <(const node b){
        return d<b.d;
    }
}p[20];
int dp[N],path[N];
int time[N],dead[N],cost[N];
void output(int x){
    if(!x) return ;
    output(x-(1<<path[x]));
    cout<<p[path[x]].s<<endl;
}
int main(){
    int T=read();
    while(T--){
        mst(dp,0);
        int n=read();
        rp(i,0,n-1) cin>>p[i].s>>p[i].d>>p[i].c;
        rp(i,1,(1<<n)-1){
            dp[i]=INF;
            RP(j,n-1,0){
                if(i&(1<<j)){
                    int last=i-(1<<j);
                    int t=time[last]+p[j].c-p[j].d>0?time[last]+p[j].c-p[j].d:0;
                    if(dp[i]>dp[last]+t){
                        dp[i]=dp[last]+t;
                        path[i]=j; 
                        time[i]=time[last]+p[j].c;
                    }
                }
            }
        }
        printf("%d\n",dp[(1<<n)-1]);
        output((1<<n)-1);
    }
    return 0;
}
发布了342 篇原创文章 · 获赞 220 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_43472263/article/details/104560580