Doing Homework HDU - 1074 (狀壓dp確定最佳分配順序)
阿新 • • 發佈:2018-11-08
題意:要求排出扣分最小的順序,如果有多個,則按字典序最小輸出。
思路:用二進位制狀壓表示有沒有選,考慮從小到大進行更新,也就是說先確定做一個作業的最小扣分,再繼續更新到做兩個作業的最小扣分.....這樣最後求(1<<n)-1的值,就是最小的扣分。
狀態轉移,列舉最後一個加入的作業即可,也就是說當前作業完成情況是sta,最後一個選的是i,那麼前一個狀態應該是sta-(1<<i).
考慮由sta-(1<<i)轉移到sta就行。
又因為給出的資料是按字典序給出的,所以我們到過來進行dp,記錄前驅,最後反向輸出就可以得到正確答案了。
總結:這型別小資料確定最佳分配順序的題目,套路就是按狀壓來dp,轉移方程很簡單,考慮列舉最後一個值選的是誰就行。
程式碼:
#include <bits/stdc++.h> using namespace std; const int maxn=1e5+9; typedef long long ll; struct Point { string name; int deadline; int cost; }p[30]; struct DP { int score; int time; int pre; int id; }dp[1<<20]; int n; int main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif int T; cin>>T; while(T--) { scanf("%d",&n); for(int i=0;i<n;i++) { cin>>p[i].name>>p[i].deadline>>p[i].cost; } int up=(1<<n)-1; __builtin_memset(dp,127,sizeof(dp)); dp[0].score=0,dp[0].time=0,dp[0].pre=-1,dp[0].id=-1; for(int s=1;s<=up;s++) { for(int i=n-1;i>=0;i--) { if(s&(1<<i)) { int oldsta=s-(1<<i); int ls=dp[oldsta].time+p[i].cost; int d=ls-p[i].deadline; int add; if(d<0) add=0; else add=d; if(dp[oldsta].score+add<dp[s].score) { dp[s].score=dp[oldsta].score+add; dp[s].time=ls; dp[s].pre=oldsta; dp[s].id=i; } } } } printf("%d\n",dp[up].score); int sta=up; stack<string> st; while(sta!=0) { string tmp=p[dp[sta].id].name; st.push(tmp); sta=dp[sta].pre; } while(st.empty()==0) { cout<<st.top()<<endl; st.pop(); } } return 0; }