hdu 1074 Doing Homework(狀壓dp)
阿新 • • 發佈:2019-01-22
對於n種家庭作業,全部做完有n!種做的順序
但是n!太大了,且對於完成作業1,2,3和1,3,2和2,1,3和2,3,1和3,2,1和3,1,2來說
完成它們消耗的天數一定是一樣的,只是完成的順序不同從而score不同
所以可以將 已經完成了某些作業的狀態 用二進位制位壓縮成一種狀態 並記錄score即可
即:狀態壓縮dp
對於到達狀態i,從何種狀態到達i呢?只需要列舉所有的作業
假如對於作業k,i中含有作業k已完成,那麼i可以由和i狀態相同的狀態僅僅是k未完成的
狀態j=i-(1<<k)來完成k到達,並且j一定比i小,如果狀態從0列舉到2^n-1那麼j一定是在i之前已經計算過的
#include <cstdio> #include <iostream> using namespace std; #define INF 0X3f3f3f3f struct node{ char name[105];//科目 int deadline,len;//截止時間,考試時長 }subject[200]; int dp[1<<15+10],times[1<<15+10],pre[1<<15+10];//dp[i]為完成i狀態(壓縮狀態)的最小分數 times[i]為完成i狀態的最小時間點 pre[i]表示當前狀態的上一個完成科目 void printPath(int loc){//遞迴列印路徑 if(!loc){ return; } printPath(loc-(1<<pre[loc])); printf("%s\n",subject[pre[loc]].name); } int main(){ int n,t,upper; scanf("%d",&t); while(t--){ scanf("%d",&n); upper=1<<n;//狀態上限 for(int i=0;i<n;i++){ scanf("%s%d%d",subject[i].name,&subject[i].deadline,&subject[i].len); } for(int i=1;i<upper;i++){//從小到大遍歷所有狀態 dp[i]=INF; for(int j=n-1;j>=0;j--){//列舉每一位是否能作為當前狀態的上一個完成科目 //輸入時按字串由小到大輸入,而每次完成j相當於把j放在後面完成,題目又要求字典序小的那個, //且下面判斷是dp[i]>dp[i-te]+score,所以從n-1開始, //如果下面判斷是dp[i]>=dp[i-te]+score,則從0開始 int te=1<<j; if(!(i&te)){ continue; } int score=times[i-te]+subject[j].len-subject[j].deadline; if(score<0) score=0;//截止時間前完成得0分 if(dp[i]>dp[i-te]+score){ dp[i]=dp[i-te]+score; times[i]=times[i-te]+subject[j].len; pre[i]=j; } } } printf("%d\n",dp[upper-1]); printPath(upper-1); } return 0; }