HDU1074 Doing Homework

题目大意:给出n分homework,每份homework有截止时间 以及需要做多少天,一份homework超出截止时间一天就罚一分,问怎么安排do homework使得罚分最少

 

思路:第一次状态压缩DP.也很纠结...这次跟文东,鑫固讨论了下,多理解了下下

因为总共有15份的homework,所以就有15!的安排方案,暴力肯定超时,但是如果用2进制来dp的话,2^15的状态数,可以接受.

这就是所谓的状态压缩DP,就是利用0辅助状态由小数推导大数,从而实现状态转移:

当前的状态由前一状态得出,因为罚分能更新的则更新.

感觉就是暴力出来的dp.

这里的2进制用得妙,因为后面的状态根据前面的状态推出来跟2进制逐渐变大是类似的.

 

AC Program:(无注释版)

#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<algorithm> 
#include<string.h>
#include<map>
#define inf 35000
#define oo 1000000000
using namespace std;
struct node
{
  string name;
  int fday;
  int lday;     
}hw[20];
struct point
{
   int pre;//前缀 
   int rescore;//罚分 
   int ttime; //当前时间      
   point()
   {
      pre=-1;
      rescore=oo;//求最小值就赋值大值 
      ttime=0;       
   }
};
void pn(int state,point dp[])
{
    
    if(state==0)
       return;
    int pre2=dp[state].pre;
    pn(pre2,dp);
    int work=(state^pre2),cnt=0;
    while(work!=1)
    {
      work=(work>>1); 
      cnt++;       
    } 
    cout<<hw[cnt].name<<endl;  
}
int main()
{
int test,n;
cin>>test;
while(test--)
{
   cin>>n;
   for(int i=0;i<n;i++)
      cin>>hw[i].name>>hw[i].fday>>hw[i].lday;
   int state=(1<<n)-1,tmp,tmptime;
   point dp[inf];
   dp[0].rescore=0;
   dp[0].ttime=0; 
   for(int i=0;i<state;i++)
   {
      for(int j=0;j<n;j++)
        {
           tmp=(1<<j); 
           if((i&tmp)==0) 
           {
              int tmpstate=i|tmp;
              int rs=dp[i].ttime+hw[j].lday-hw[j].fday;
              if(rs<0)
                 rs=0;
              rs+=dp[i].rescore;
              int tt=dp[i].ttime+hw[j].lday;
              if(dp[tmpstate].rescore>rs) 
              {
                      dp[tmpstate].pre=i;
                      dp[tmpstate].rescore=rs;
                      dp[tmpstate].ttime=tt;                      
              }     
           }      
        }        
   }
   cout<<dp[state].rescore<<endl;
   pn(state,dp);              
} 
//system("pause");
return 0;} 


 

 

AC Program(注释版):

#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<algorithm> 
#include<string.h>
#include<map>
#define inf 35000
#define oo 1000000000
using namespace std;
struct node
{
  string name;
  int fday;
  int lday;     
}hw[20];
struct point
{
   int pre;//前缀 
   int rescore;//罚时 
   int ttime; //当前时间      
   point()
   {
      pre=-1;
      rescore=oo;//求最小值就赋值大值 
      ttime=0;       
   }
};


void pn(int state,point dp[])
{
    
    if(state==0)//0是辅助状态,并且是开始状态 
       return;
    int pre2=dp[state].pre;
    pn(pre2,dp);
    int work=(state^pre2),cnt=0;
    while(work!=1)
    {
      work=(work>>1); 
      cnt++; 
    } 
    cout<<hw[cnt].name<<endl;  
}
int main()
{
int test,n;
cin>>test;
while(test--)
{
   cin>>n;
   for(int i=0;i<n;i++)
      cin>>hw[i].name>>hw[i].fday>>hw[i].lday;
   int state=(1<<n)-1,tmp,tmptime;
   point dp[inf];
   dp[0].rescore=0;//开始为0很符合意思. 
   dp[0].ttime=0; 
   for(int i=0;i<state;i++)//i不会到state,利用滚动可以dp取到state.这就是0的作用 
   {
      for(int j=0;j<n;j++)//注意1是左移n-1次 
        {
           tmp=(1<<j);
           //检索当前这个工作做了没有,如果已经做了,则跳过
           //如果没有做的话,则加入新的工作去更新当前的dp[i]这个状态.
           //cout<<"tmp i&tmp "<<tmp<<" "<<(i&tmp)<<endl; 
           if((i&tmp)==0)//新状态,记录下来 
           {
              //没有做过j,就把j加进去.加进来的状态不管加没加过分数都 
              //直接比较看大小,这是初始化的功劳 
              //感觉就是用前面的去推倒后面的,这就是,2进制的妙
              //用小数的状态去推导大数的状态
              int tmpstate=i|tmp;//利用或逻辑运算符加进状态
              
              int rs=dp[i].ttime+hw[j].lday-hw[j].fday;//当前i|j这个状态的罚分
              if(rs<0)
                 rs=0;
              //if(i!=0) dp[0]的赋值省了一部分的功夫 
              rs+=dp[i].rescore;//当前状态最后的罚分=(前一状态的罚分+当前的罚分) 
              int tt=dp[i].ttime+hw[j].lday;//i|j这个状态的当前时间                
              if(dp[tmpstate].rescore>rs) 
              {
                      dp[tmpstate].pre=i;//前驱不能随便改,不然就不能字典序了 
                      dp[tmpstate].rescore=rs;
                      dp[tmpstate].ttime=tt;                      
              }   
                            
               
              
           }      
        }        
   }
   cout<<dp[state].rescore<<endl;
   pn(state,dp);             
} 
//system("pause");
return 0;} 



 

你可能感兴趣的:(HDU1074 Doing Homework)