1. 程式人生 > 實用技巧 >狀 壓 d p

狀 壓 d p

原來一直沒太搞懂,今天大力搞了搞,感覺比較可了
latex取反是\sim

\[把數拆成二進位制數 所以我們最多隻需要2^{n+1}-1的十進位制數就好(二進位制形式是n個1)\\ 1.判斷一個數字x二進位制下第i位是不是1(假設最低為第1位)\\ if(1 << (i-1)\&x) 操作\\ 2.將一個數字x二進位制第i位改成1\\ x = x | (1<<(i-1))\\ 3.將一個數字x二進位制下第i為改成0\\ x=x\&(\sim(1<<(i-1)))取反符號是sim\\ 4.將一個數二進位制下最靠右的一去掉\\ x = x\&(x-1) \]

狀壓裡提到狀態的一維肯定是1<<某某

p1896

\[互不侵犯在 n×n(1\le n\le10) 的棋盤上放 k(0\le k<n^2)個國王,國王可攻擊相鄰的8個格子,求使它們無法互相攻擊的方案總數。\\ 考慮到每行每列之間都有互相的約束關係。因此,我們可以用行和列作為另一個狀態的部分。\\ 我們的三個狀態就有了:第幾行(用i表示),此行放什麼狀態(用j表示),包括這一行已經使用了的國王數用s表示\\ f[i][j][s]=\sum f[i-1][k][s-num[j]]\\ 首先的問題就是,上文中的k和j怎麼表示?\\ 假設現在第i行的放置狀態是這樣的:0 0 1 0 1 0 0 1(1代表有國王,0代表沒有)\\ 轉換成十進位制就是: 41(10) dp:f[i][41][s]\\ 不過對於每一行的可用狀態,我們可以預處理一下,預處理每一行可用可以避免麻煩,還可以優化時間複雜度。\\ 預處理程式碼: \]

inline void init(){
cnt=0;
for(int i=0;i<(1<<n);++i)
{
	if(i&(i<<1))continue;
	int sum=0;
	for(int j=0;j<n;++j)
	   if(i&(1<<j))++sum;
	s[++cnt]=i;
	num[cnt]=sum;
}return;}

\[s陣列時用來記錄可用狀態的十進位制\\ num陣列用來記錄該狀態的國王數上面例子 國王數為3\\ i是列舉所有狀態數。假設n=3,那麼i列舉的就是:\\ 000,001,010,011,100,101,110,111\\ 分別對應十進位制0,1,2,3,4,5,6,7\\ 所以是 i<(1<<n) (i不能到2^3(8))\\ if(i\&(i<<1))continue;判斷相鄰國王\\ \]

//AC程式碼
#include<bits/stdc++.h>
using namespace std;
int n,m,cnt,MAX;
long long dp[10][200][100];
int can[1000],num[2000];
int getsum(int x)
{
    int ret=0;
    while (x) ret+=(x&1),x>>=1;
    return num[cnt]=ret;
}//預處理函式 
int main()
{
    register int i,j,k,l,x,y;
    long long ans=0; 
    scanf("%d %d",&n,&m);
    MAX=(1<<n)-1;
    for (i=0;i<=MAX;i++) 
	if (!(i&(i<<1))) can[++cnt]=i,dp[1][cnt][getsum(i)]=1;//預處理 
    for (i=2;i<=n;i++)
    {
        for (j=1;j<=cnt;j++)
        {
            x=can[j];
            for (k=1;k<=cnt;k++)
            {
                y=can[k];
                if ((x&y)||(x&(y<<1))||(x&(y>>1))) continue; 
                for (l=0;l<=m;l++) dp[i][j][num[j]+l]+=dp[i-1][k][l];
            }
        }
    }
    for (i=1;i<=cnt;i++) ans+=dp[n][i][m];
    printf("%lld",ans);
}

p3694 邦邦的大合唱站隊

\[我們可以設狀態i為當前已經排列好的樂隊編號集合\\ sum[i][j]表示前i個人有幾個屬於樂隊j\\ 列舉l,r,則有f[i|(1<<j)]=min(f[i|(1<<j)],f[i]+(r-l-(sum[r][j]-sum[l][j]))) \]

同理eee