1. 程式人生 > 其它 >Necklace HDU-3091(狀壓DP)

Necklace HDU-3091(狀壓DP)

技術標籤:動態規劃dp演算法

Necklace HDU-3091

點選跳轉↑

  • 題設:多組輸入,每個樣例輸入n(珠子的總數),m(可連線的珠子對的總數)( 1<=n<=18,m<=n*n )。接下來輸入m行,每行兩個數a和b ( 1<=a,b<=n ),代表編號為a和b珠子是可連線的。
    問:在m條可連線關係下將這n個珠子串成項鍊,共有多少種結果。
  • 思路:這是有限制條件的問題求解,易發現可使用狀壓dp,為方便位運算處理,現將所有珠子編號都-1(從零開始)。
    1、定義dp【i】【j】代表將編號為i的珠子串起來,此時的狀態為j(j的二進位制下有n位,0代表未使用,1代表已使用)。
    2、因為最終會成環,可隨意定義一個珠子為初態,當前狀態j就對應為1,若使用編號為0的珠子,則初態dp【0】【1】= 1。
    3、對於所有珠子的使用狀態,用位運算(i<<n)列舉,每個狀態下,都對所有已使用的珠子剩餘未使用的珠子間進行判斷,可連線則更新dp陣列的值。
    4、最後的答案就是珠子i(1-n)中,能和編號為0的珠子相連(link【0】【i】==1),dp【i】【(1<<n)】的總和。
    注意:位運算一定要一步一括號,dp陣列和最終答案ans都可能非常大,需要開long long防止溢位。
  • 程式碼
#include<bits/stdc++.h>
#define
ll long long
#define LL unsigned long long #define up_b upper_bound #define low_b lower_bound #define all(a) begin(a),end(a) #define mem(a,n) memset(a,n,sizeof(a)) #define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); using namespace std; priority_queue <int,vector<int>,less<int
> > QM; const int INF= 0x3f3f3f3f; const int maxn= 2e5+5; int n,m,link[20][20]; ll dp[20][1<<18]; int main() { IOS; while(cin>>n>>m) { int a,b; mem(dp,0); mem(link,0); while(m--) { cin>>a>>b; link[a-1][b-1]=1; link[b-1][a-1]=1; } //dp[i][j]表示將編號為i的珠子連線後當前狀態為j dp[0][1]=1; for(int i=1;i<(1<<n);i++)//列舉所有狀態 { for(int j=0;j<n;j++)//列舉珠子編號 { if(dp[j][i]!=0)//編號為j的珠子在狀態中已連線 { for(int k=0;k<n;k++)//列舉其他未連線的珠子 { if((i&(1<<k))==0 && link[j][k])//沒放k,且k能和j連線 { dp[k][(i|(1<<k))] += dp[j][i]; } } } } } ll ans=0; for(int i=1;i<n;i++) { if(link[0][i]) ans += dp[i][(1<<n)-1]; } cout<<ans<<endl; } return 0; }