UVA10817 Headmaster's Headache (狀壓DP)
阿新 • • 發佈:2018-12-13
題意:
S門課程,M個教師,N個待聘教師。每個教師都有工資數和能夠教授課程種類,要求在M個教師全部選擇的基礎上,再選擇性的僱傭一部分待聘教師,在保證S門課程都至少有2人教授的前提下,付出的工資最少。s<8
思路:
我們通過課程資訊來確定最終結果,用三個狀態s0,s1,s2的二進位制位來代表課程當前的狀態,s0中的對應二進位制位為1表示該課程沒有人教授,s1為1表示該課程只有一個人教授,s2為1表示該課程至少兩個人教授。
我們通過列舉每位待聘教師來確定最終的答案,很明顯轉移方程為 dpi = min(dp(i+1,news1,news2)+val[ i ] ,dp( i+1, s1,s2) )
即僱傭或者不僱傭該教師的最小值。
因為三個二進位制狀態的關係,所以我們只需要保留s1和s2就可以很輕易的算出s0 。s0= (s1|s2) ^(全為1);
接下來記憶化搜尋一下就行。
程式碼如下:
#include <iostream> #include <algorithm> #include <cstdio> #include <cstring> #include <vector> #include <string> #include <map> #include <cstring> using namespace std; const int maxn = 150; const int maxs = (1<<8)+5; const int inf = 0x3f3f3f3f; int dp[maxn][maxs][maxs]; int S,M,N; int val[maxn]; int s[maxn]; int all; int DP(int t,int s1,int s2) { if(s2==all) return 0; if(t==N) { if(s2==all) return 0; return inf; } int s0 = ((s1|s2)^all); int& res = dp[t][s1][s2]; if(res!=-1) return res; int nows = s[t]; int news1=s1,news2=s2; for(int x=0; x<S; x++) { if(!(nows&(1<<x))) continue; if(s0&(1<<x)) { news1 = (news1|(1<<x)); } else if(news1&(1<<x)) { news1 = (news1^(1<<x)); news2 = (news2|(1<<x)); } } //cout<<" t="<<t<<" s1="<<s1<<" s2="<<s2<<" new1= "<<news1<<" new2="<<news2<<endl; res = min(DP(t+1,s1,s2),DP(t+1,news1,news2)+val[t]); return res; } char cnt[1000]; void getm(int &s1,int& s2,int &res) { int x=0; int okv = 0; int len = strlen(cnt); int s0 = ((s1|s2)^all); for(int i=0; i<len; i++) { if(cnt[i]==' ') { if(!okv) { res+= x; x = 0; } else { x--; if(s0&(1<<x)) { s0 = (s0^(1<<x)); s1 = (s1|(1<<x)); } else if(s1&(1<<x)) { s1 = (s1^(1<<x)); s2 = (s2|(1<<x)); } x = 0; } okv = 1; } else x = x*10+ cnt[i]-'0'; } } void getn(int t) { int x=0; int okv = 0; int len = strlen(cnt); int nows = 0; for(int i=0; i<len; i++) { if(cnt[i]==' ') { if(!okv) { val[t] = x; x = 0; } else { x--; nows|=(1<<x); x = 0; } okv = 1; } else x = x*10+ cnt[i]-'0'; } // cout<<t<<" nows"<<nows<<endl; s[t] = nows; } int main() { while(scanf("%d%d%d",&S,&M,&N) && S!=0) { getchar(); int len; all = (1<<(S))-1; int s1 = 0; int s2 = 0; int ans = 0; for(int i=0; i<M; i++) { gets(cnt); len = strlen(cnt); cnt[len]=' '; cnt[++len]='\0'; getm(s1,s2,ans); } for(int i=0; i<N; i++) { gets(cnt); len = strlen(cnt); cnt[len]=' '; cnt[++len]='\0'; getn(i); } // cout<<s1<<" "<<s2<<" all="<<all<<endl; memset(dp,-1,sizeof(dp)); ans+=DP(0,s1,s2); printf("%d\n",ans); } return 0; }
看了看紫書的思路,一遍過還是很開心的。