Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 3595 Accepted Submission(s): 1424
#include<iostream> #include<string.h> #include<stdio.h> #define N (1<<15)+5 #define M 120 #define INF 0x3f3f3f3f using namespace std; int dp[N];//dp[i]表示状态i时最少扣的分数 struct node { char name[M]; int d,c;//截止日期,花费时间 }a[20]; int path[N];//path[i]=k表示i状态是由k状态转移过来的 int t,n; /* 打印路径就是在状态转移的时候用一个数组记录下来,一个状态是由哪一个状态成功转移过来的,然后最后打印路径的时候就是反着这个根据用递归输出出来就行了 */ void print(int x) { if(x==0) return; int t=0; for(int i=0;i<n;i++) { if( (x&(1<<i)) !=0 && (path[x]&(1<<i)) ==0 )//有这个状态有交际,并且能补全上一个状态 { t=i; break; } } print(path[x]); printf("%s\n",a[t].name); } /* 状态的转移:先枚举二进制的状态,每枚举到一个状态的时候,在这个状态中遍历所有课程,若这门课程完成了那么已经包括在这个状态中了,如果没完成就要 像01背包那样讨论完成和不完成的罚时到底哪个小,假如现在不完成的罚时小,那么这门课就先不完成,后面在完成。 */ int main() { //freopen("in.txt", "r", stdin); scanf("%d",&t); while(t--) { scanf("%d\n",&n); for(int i=0;i<n;i++) scanf("%s %d %d\n",&a[i].name,&a[i].d,&a[i].c); memset(dp,INF,sizeof dp); memset(path,0,sizeof path); dp[0]=0;//什么课也没开始上罚时当然是0了 int tol=(1<<n); for(int i=0;i<tol;i++)//枚举状态 { for(int j=n-1;j>=0;j--)//枚举那门课 { if((i&(1<<j))==0)//这门课上没上,找出每一个当前功课没完成的状态,然后由这个状态转移到完成了当前课程的状态 { int s=0; for(int k=0;k<n;k++)//计算这个状态到现在花费的时间 if(i&(1<<k))//这门课上了 s+=a[k].c; s+=a[j].c;//再加上j这门课花费的时间 int time=s-a[j].d;//计算罚时 if(time<0) time=0; if(dp[i|(1<<j)]>dp[i]+time) { dp[i|(1<<j)]=dp[i]+time; path[i|(1<<j)]=i;//如果这个状态转移过来了,就记录下来是由哪个状态转移过来的 } //cout<<dp[i|(1<<j)]<<endl; } } } //for(int i=0;i<tol;i++) // cout<<path[i]<<" "; //cout<<endl; printf("%d\n",dp[tol-1]); print(tol-1); } return 0; }
HDU 1074 Doing Homework (状态压缩DP)
原文:http://www.cnblogs.com/wuwangchuxin0924/p/5741126.html