題意:構造一個點數不超過20無重邊無自環的有向圖,節點編號從0到n-1,使得從0到n-1的哈密頓路徑數量恰好為k。
感覺完全沒有思路。。不知道怎麼構造。
去看了下別人的代碼,原來可以做成1<-2<-…<-18一條路徑,然後1-18都向編號比它大的節點連邊。這樣的話往回走就只有唯一的路徑,所以假如說我當前在i,走到了j,j>i,那麼就需要立刻從j到j-1直到i+1,然後再往後走。這樣就相當於每個節點有選或不選兩種狀態,如果選的話就是從前面第一個比它小的選的節點跳過來,不選的話就是從i+1過來。所以如果有0->i,就相當於貢獻了 218−i−1(1≤i<18) 2^{18-i-1}(1\le i(因為18是必選的,19不能回到18)。
既然不會做的話,那就亂搞一下吧~
假如說隨便確定了一個圖,怎麼求路徑數量呢。那就需要 O(220202) O(2^{20}20^2)的狀壓dp。不過我們可以先不求0->n-1的哈密頓路徑數量,可以先求 i−>19,1≤i<19 i->19,1\le i,然後再連從0出去的邊,這樣的話就相當於是問是否存在一個子集和恰好為k。
我們知道 (189) \binom{18}{9}是一個非常大的數了,所以如果i->19的路徑數量是在 k9 k\over 9附近隨機,那麼就很容易一不小心就湊出個k來。所以我們可以二分邊的總數,直到找到一個邊的總數在那裡隨機 i−>19 i->19的路徑數量的平均值會離 k9 k\over 9比較近,然後就在那裡一直隨機。
這樣的話時間複雜度是 O(2nn2logn)≈109 O(2^nn^2\log n)≈10^9的。所以dp的常數比較要用心卡一下才行。。。我一開始算錯了時間複雜度以為是 O(2nnlogn) O(2^nn\log n)的就沒管,結果範例都過不去。。。
代碼:
#include<bits/stdc++.h>using namespace std;const int n=20,N=20;typedef long long LL;class HamiltonianConstruction{ public: pair<int,int> edge[N*N]; int etot; LL f[1<<N][N]; LL s[1<<N]; int prev[N]; int bit[1<<N]; inline void cal(int mid) { random_shuffle(edge,edge+etot); memset(prev,0,sizeof(prev)); for(int i=mid;i--;)prev[edge[i].second-1]|=1<<edge[i].first-1; memset(f,0,sizeof(f)); f[1<<n-2][n-2]=1; for(int i=1;i<1<<n-1;++i) for(int j=i,x;j;j^=1<<x) if(f[i][x=bit[j&-j]]) for(int k=prev[x]&~i,y;k;k^=y) { y=k&-k; f[i|y][bit[y]]+=f[i][x]; } for(int i=n-2;i--;)s[1<<i]=f[(1<<n-1)-1][i]; for(int i=1;i<1<<n-2;++i)s[i]=s[i^i&-i]+s[i&-i]; } inline vector<string> construct(int k) { for(int i=0;i<n;++i)bit[1<<i]=i; for(int i=n-1;--i;) for(int j=n;--j;) if(i!=j) edge[etot++]=make_pair(i,j); vector<string> ans(N,string(N,'N')); int l=0,r=100; while(r-l>1) { int mid=l+r>>1; cal(mid); for(int i=1<<n-2;i--;) if(s[i]==k) { for(int j=mid;j--;)ans[edge[j].first][edge[j].second]='Y'; for(int j=n-2;j--;) if(i>>j&1) ans[0][j+1]='Y'; return ans; } LL sum=0; for(int i=n-2;i--;)sum+=s[1<<i]; if(sum>=k<<1)r=mid; else l=mid; } for(;;) { cal(r); for(int i=1<<n-2;i--;) if(s[i]==k) { for(int j=r;j--;)ans[edge[j].first][edge[j].second]='Y'; for(int j=n-2;j--;) if(i>>j&1) ans[0][j+1]='Y'; return ans; } } }};
總結:
①構造的時候可以考慮一些特殊情況,或者根據題目中的特殊性質。比如說構造一個圖,就可以先考慮一條鏈、完全圖、樹。
②如果沒有構造的思路,不妨試試隨機化。
③一定要算好時間複雜度。