hdu 1074 Doing Homework (狀壓dp+搜尋)
阿新 • • 發佈:2019-01-08
演算法核心:狀態壓縮DP
大意:
有n門課程作業,每門作業的截止時間為D,需要花費的時間為C,若作業不能按時完成,每超期1天扣1分。
這n門作業按課程的字典序先後輸入
問完成這n門作業至少要扣多少分,並輸出扣分最少的做作業順序
PS:達到扣分最少的方案有多種,請輸出字典序最小的那一組方案
分析:
n<=15,由題意知,只需對這n份作業進行全排列,選出扣分最少的即可。
用一個二進位制數儲存這n份作業的完成情況,第1.。。。n個作業狀況分別
對應二進位制數的第0,1.。。。。,n-1位則由題意,故數字上限為2^n
其中 2^n-1即為n項作業全部完成,0為沒有作業完成。。。
用dp[i]記錄完成作業狀態為i時的資訊(所需時間,前一個狀態,最少損失的分數)。
遞推條件如下
1.狀態a能做第i號作業的條件是a中作業i尚未完成,即a&i=0。
2.若有兩個狀態dp[a],dp[b]都能到達dp[i],那麼選擇能使到達i扣分小的那一條路徑,若分數相同,轉入3
3.這兩種狀態扣的分數相同,那麼選擇字典序小的,由於作業按字典序輸入,故即dp[i].pre = min(a,b);
初始化:dp[0].cost = 0;dp[0].pre=-1;dp[0].reduced = 0;
大意:
有n門課程作業,每門作業的截止時間為D,需要花費的時間為C,若作業不能按時完成,每超期1天扣1分。
這n門作業按課程的字典序先後輸入
問完成這n門作業至少要扣多少分,並輸出扣分最少的做作業順序
PS:達到扣分最少的方案有多種,請輸出字典序最小的那一組方案
分析:
n<=15,由題意知,只需對這n份作業進行全排列,選出扣分最少的即可。
用一個二進位制數儲存這n份作業的完成情況,第1.。。。n個作業狀況分別
對應二進位制數的第0,1.。。。。,n-1位則由題意,故數字上限為2^n
其中 2^n-1即為n項作業全部完成,0為沒有作業完成。。。
用dp[i]記錄完成作業狀態為i時的資訊(所需時間,前一個狀態,最少損失的分數)。
遞推條件如下
1.狀態a能做第i號作業的條件是a中作業i尚未完成,即a&i=0。
2.若有兩個狀態dp[a],dp[b]都能到達dp[i],那麼選擇能使到達i扣分小的那一條路徑,若分數相同,轉入3
3.這兩種狀態扣的分數相同,那麼選擇字典序小的,由於作業按字典序輸入,故即dp[i].pre = min(a,b);
初始化:dp[0].cost = 0;dp[0].pre=-1;dp[0].reduced = 0;
最後dp[2^n-1].reduced即為最少扣分,課程安排可遞迴的輸出
感想:
1、遞迴輸出的時候將狀態轉化為對應課程下標時要注意,由於是下標為0的表示第一門課程,所以要減去1。
也可以
void out(int state)
{
int x=DP[state].pre^state;
int cnt=0;
x>>=1;
while(x)
{
cnt++;
x>>=1;
}
if(DP[state].pre!=0)
out(DP[state].pre);
printf("%s\n",course[cnt].name);
}
2、二進位制的運算一定要加括號啊啊啊啊!!!
#include<cstdio> #include<iostream> #include<cstring> #define MAX 1<<16 using namespace std; struct Course { int cost; int deadline; int name[105]; }course[16]; struct node { int pre; int reduced; int cost; }DP[MAX]; bool vis[MAX]; void out(int state) { int x=DP[state].pre^state; int cnt=0; while(x) { x>>=1; cnt++; } if(DP[state].pre!=0) out(DP[state].pre); printf("%s\n",course[cnt-1].name); } int main() { int T,i,j,n,cc,reduce,curtmp,cur; scanf("%d",&T); while(T--) { memset(vis,0,sizeof(vis)); scanf("%d",&n); for(i=0;i<n;i++) scanf("%s%d%d",&course[i].name,&course[i].deadline,&course[i].cost); DP[0].pre=-1; DP[0].cost=0; DP[0].reduced=0; vis[0]=true; int zz=(1<<n)-1; //位運算加括號!!! for(i=0;i<zz;i++) { for(j=0;j<n;j++) { cur=1<<j; if((i&cur)==0) { curtmp=i|cur; cc=DP[i].cost+course[j].cost; DP[curtmp].cost=cc; reduce=cc-course[j].deadline; if(reduce<0) reduce=0; reduce+=DP[i].reduced; if(vis[curtmp]) { if(reduce<DP[curtmp].reduced) { DP[curtmp].reduced=reduce; DP[curtmp].pre=i; } } else { vis[curtmp]=true; DP[curtmp].pre=i; DP[curtmp].reduced=reduce; } } } } printf("%d\n",DP[zz].reduced); out(zz); } return 0; }