状压DP

来源:互联网 发布:数据库设计与开发规范 编辑:程序博客网 时间:2024/05/21 19:26

HDU1074,这题的大意是一个人需要完成他的作业,但是他的老师给他定了期限,每超过期限一天,就扣一分,问你如何安排做作业的顺序,才能使扣的分数最少,输出要扣的最小分数,并输出完成作业的顺序。(科目数目n<=15)


如果把n个科目进行排序,那么复杂度就有n!,肯定爆炸。其实每一个状态对于一种科目,只有两种可能,一是这个状态下做这个科目,二是不做这个科目,就是1,0两种状态,这样,我们就可以利用二进制很轻松的表示出来(比如101就是这个状态已经做了第一个科目和第三个科目)。利用二进制把状态用0,1表示,就是所谓的状态压缩,用这种方法,我们就可以在(2^n)*n的复杂度下解决这个问题,我们可以明显的看出,状态压缩一般也只能解决n<=15的问题,再大的话,时间也是不够的。


#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>using namespace std;const int maxn = (1<<15) + 5;int dp[maxn];int d[20],f[20];//数组d是期限,数组f是完成需要的时间int pre[maxn],t[maxn];//数组pre是用来记录当前状态是由哪个状态推过来的,数组t是记录到当前状态用了多长时间char a[20][110];void output(int x){    if(!x)        return;    output(x-(1<<pre[x]));    printf("%s\n",a[pre[x]]);}int main(void){    int T,n,m,i,j,inf = 1e9;    scanf("%d",&T);    while(T--)    {        scanf("%d",&n);        for(i=0;i<n;i++)            scanf("%s %d %d",a[i],&d[i],&f[i]);        m = 1<<n;        for(i=1;i<m;i++)//状态0表示一个科目都没完成,状态2^n-1表示全部科目都已完成        {            dp[i] = inf;            for(j=n-1;j>=0;j--)            {                int tem = 1<<j;//表示第j个科目                if((tem&i) == 0)//如果状态i还没有完成j,肯定不能由i-j推过来                    continue;                int s = t[i-tem] + f[j] - d[j];//表示扣的分数                if(s < 0)//扣的分数最小为0                    s = 0;                if(dp[i] > dp[i-tem] + s)                {                    dp[i] = dp[i-tem] + s;                    t[i] = t[i-tem] + f[j];                    pre[i] = j;                }            }        }        printf("%d\n",dp[m-1]);        output(m - 1);//这里用了递归的方法进行输出    }    return 0;}
/*
input:
2
3
Computer 3 3
English 20 1
Math 3 2
3
Computer 3 3
English 6 3
Math 6 3


output:
2
Computer
Math
English
3
Computer
English
Math
*/

0 0