• hdoj1074--Doing Homework (DP 状态压缩)


    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1074

    思路:

    看着数据很小,15,但是完成的顺序有15!情况,这么大的数据是无法实现的。上网查才知道要用状态压缩,用二进制表示状态,比如n=3时:111表示3門全部完成,011表示完成第1,2門的状态,000表示一門都没完成的情况。这样压缩之后最多就只有1<<15种状态了,然后直接dp就可以了。大体思路是从1状态到(1<<n-)1依次遍历,寻找上一个状态,使得到达此状态时总扣分最小,寻找上一个状态时j从n-1开始到0倒着遍历,这样才能保证最终结果是字典序最小的,这里要仔细想一下,比如寻找111的上一个状态,若j=1和j=0对应的上一个状态同时使111这个状态的总扣分最小,因为输入的顺序即按字典序来的,所以j=1对应的作业字典序要大于j=0的,所以要让j=0对应的作业先于j=1的完成,所以应该选择j=1作为上一个状态,故应该倒着遍历。详见代码。

    AC代码如下:

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 
     4 const int inf=0x3f3f3f3f;
     5 struct course{
     6     char name[105];
     7     int d,c;
     8 }a[20];
     9 
    10 struct node{  //上一个状态,扣分值,总用时,该状态所做的作业编号
    11     int pre,redu,cost,cou;
    12 }dp[1<<15+5];
    13 
    14 int T,n,maxn;
    15 
    16 void print(int p){   //递归逆向输出
    17     if(p){
    18         print(dp[p].pre);
    19         printf("%s
    ",a[dp[p].cou].name);
    20     }
    21 }
    22 
    23 int main(){
    24     scanf("%d",&T);
    25     while(T--){
    26         scanf("%d",&n);
    27         maxn=1<<n;    
    28         for(int i=0;i<n;i++)
    29             scanf("%s%d%d",a[i].name,&a[i].d,&a[i].c);
    30         memset(dp,0,sizeof(dp));
    31         for(int i=1;i<maxn;i++){
    32             dp[i].redu=inf;    //找最小的扣分,所以初始化为inf
    33             for(int j=n-1;j>=0;j--){   //倒着遍历,为了结果字典序最小
    34                 int t=1<<j;
    35                 if(i&t){
    36                     int tmp=i-t;    //上一个状态
    37                     int tt=dp[tmp].cost+a[j].c-a[j].d;
    38                     if(tt<0) tt=0;  //扣分值若小于0则置0
    39                     if(dp[tmp].redu+tt<dp[i].redu){
    40                         dp[i].redu=dp[tmp].redu+tt;
    41                         dp[i].pre=tmp;
    42                         dp[i].cost=dp[tmp].cost+a[j].c;
    43                         dp[i].cou=j;
    44                     }
    45                 }
    46             }
    47         }
    48         printf("%d
    ",dp[maxn-1].redu);
    49         print(maxn-1);
    50     }
    51     return 0;
    52 }
  • 相关阅读:
    HDU 2108 Shape of HDU (判断是不是凸多边形 叉乘)
    三,对于printf函数和C语言编程的初步拓展
    二,养成良好的写代码习惯
    一,彻底理解第一个C语言程序 Hello World
    归并排序(看了别人的博客明白了也写个博客,希望这样不算抄袭~)
    汉诺塔
    最小生成树
    堆排序
    二叉排序树
    双关键字快排
  • 原文地址:https://www.cnblogs.com/FrankChen831X/p/10389580.html
Copyright © 2020-2023  润新知