JZOJ 5230. 隊伍統計
阿新 • • 發佈:2019-01-28
desc 所有 pre wid 分析 想要 一個 矛盾 image
題目
分析
-
一道狀壓DP(不會啊啊啊啊)
- 考試時,穩穩地打了暴力 30
- 後來才理解
- 狀壓,顧名思義就是要將一些狀壓想辦法壓縮起來(可以壓,也可以刪)。其中這些狀態都滿足相似性和數量很多。這樣才好壓而且壓得有意義。
- 所以一般基礎的狀壓就是將一行的狀態壓成一個數,這個題用數的二進制形式反映了一種情況。所以位運算可以幫助我們解決很多問題。
- 這道題,我們將沖突的數用二進制放在a數組裏
- 然後我們用二進制數來代表n位的選取情況
- 進行DP f[i][j] 表示在二進制表示的第i種選取情況下有j種矛盾的情況數
- 轉移方程就是f[當前已改變的集合][j+沖突個數]+=沒改變前的種數f[i][j]
- 然後對於每個i 我們還要枚舉對第x位進行一次選取,計算出答案
- (位運算分析具體在代碼裏)
代碼
1 #include<fstream> 2 #define M 1000000007 3 #define cin fin 4 #define cout fout 5 using namespace std; 6 ifstream fin("count.in"); 7 ofstream fout ("count.out"); 8 int a[21],f[1<<21][21],t[1<<21]; 9 int n,m,k; 10 void take() //計算所有集合中1的個數 也就是選取的個數 11 { 12 for (int i=0;i<=(1<<n)-1;i++) 13 { 14 int ii=i; 15 while (ii) 16 { 17 t[i]++; 18 ii-=(ii&(-ii)); //(ii&(-ii))找到最後的1位置,然後減去 19 } 20 } 21 } 22 int main () 23 { 24 ios::sync_with_stdio(false); //cin優化 25 cin>>n>>m>>k; 26 for (int i=1,u,v;i<=m;i++) 27 { 28 cin>>u>>v; 29 a[u]|=(1<<(v-1)); //用二進制,來反應它的沖突 例: 30 } // 首先數組為 00000 31 take(); // 現在與3沖突了 00100 第三位 1 32 f[0][0]=1; // 又與 4 沖突了 01100 第四位變1 33 for(int i=0;i<=(1<<n)-1;i++) //所以這就表示出來了,當前數u與其他數的沖突情況 34 for(int j=0;j<=k;j++) 35 if(f[i][j]) 36 for(int x=1;x<=n;x++) 37 if((i&(1<<(x-1)))==0) // 表示當前集合與要變的位置沒有變過 38 /*因為t記錄的是(上面)t[i&a[x]]為沖突個數*/if(j+t[i&a[x]]<=k) //例 1010 x=2 1010 & 0100 無改變 39 f[i|(1<<(x-1))][j+t[i&a[x]]]=(f[i|(1<<(x-1))][j+t[i&a[x]]]+f[i][j])%M; 40 int ans=0; 41 for (int i=0;i<=k;i++) 42 ans=(ans+f[(1<<n)-1][i])%M; 43 cout<<ans; 44 }
JZOJ 5230. 隊伍統計