2018ICPC南京網路賽 E
阿新 • • 發佈:2018-12-10
題型:動態規劃(狀壓DP) 動態規劃夠噁心吧,狀態壓縮動態規劃瞭解一下?
其實題目的大致思想還是不變的:最優子集,狀態轉移,邊界條件
狀壓DP其實就是將每種狀態轉化為二進位制計法,所以在學習狀壓DP時首先得把位操作整明白了
舉幾個常用的:<< : 左移 >> : 右移 & : 且 | : 或 ^:異或 ~:取反
&:同為1 取1 否則取0
|: 同為0 取0 否則取1
^:相同取0 不同取1 (取反之後就是 相同取1 不同取0了啊)
題目大意:做題,怎麼做得分最多 ,但是做一道題前必須某些固定的題目唄做完,經過相應的計算公式得到你的最終得分。
題目分析:第一反應:貪心 第二反應:貪心不對;
DP陣列:dp[i] 就表示狀態為 i 的時候的最高得分 ,i 表示已經做過的題目 ,用二進位制表示。
舉個栗子:當i=3時 ,二進位制為11,就表示做完了1 ,2兩題 所以說 ,如果有五道題,那麼所有的狀態該是多少?
是不是 11111 (1<<(5-1));
狀態轉移方程: dp[i]=max(dp[i],dp[i^(1<< j-1)]+a*c+b) c表示第幾個做的該題(詳見程式碼)
臨界當然就是dp為0了;
上程式碼:
#include<bits/stdc++.h> using namespace std; int n; const int maxn=25; int a[maxn],b[maxn]; vector<int> pre[maxn]; int dp[1<<20]; int main() { int ans=0;; cin>>n; for(int i=1;i<=n;i++){ int si,temp; cin>>a[i]>>b[i]>>si; while(si--){ cin>>temp; pre[i].push_back(temp); } } memset(dp,0,sizeof(dp)); for (int i=0;i<(1<<n);i++){ int flag=0; for(int j=1;j<=n;j++){ if(!(i&(1<< j-1))) continue; for (int k=0;k<pre[j].size();k++){ if(!(i & (1<< pre[j][k]-1))) { flag=1; break; } } if(flag) break; } if(flag) continue; for(int j=1;j<=n;j++){ if(!(i&(1<< j-1))) continue; int c=0; int s=i; while(s){ if(s&1) c++; s>>=1; } dp[i]=max(dp[i],dp[i^(1<< j-1)]+a[j]*c+b[j]); ans=max(ans,dp[i]); } } cout<<ans<<endl ; }
仔細讀應該還是不難理解的,具體細節或者你有想hack的資料可以試一下,觀察一下細節。
歡迎加我微信鴨!