1. 程式人生 > >5299. [CQOI2018]解鎖屏幕【狀壓DP】

5299. [CQOI2018]解鎖屏幕【狀壓DP】

online 沒有 轉移 一個點 新的 desc ++ scan 解鎖

Description

使用過Android手機的同學一定對手勢解鎖屏幕不陌生。Android的解鎖屏幕由3x3個點組成,手指在屏幕上畫一條 線將其中一些點連接起來,即可構成一個解鎖圖案。如下面三個例子所示: 技術分享圖片 畫線時還需要遵循一些規則 1.連接的點數不能少於4個。也就是說只連接兩個點或者三個點會提示錯誤。 2.兩個點之間的連線不能彎曲。 3.每個點只能"使用"一次,不可重復。這裏的"使用"是指手指劃過一個點,該點變綠。 4.兩個點之間的連線不能"跨過"另一個點,除非那個點之前已經被"使用"過了。 對於最後一條規則,參見下圖的解釋。左邊兩幅圖違反了該規則:而右邊兩幅圖(分別為2→4→1→3→6和→5→4→1→9→2) 則沒有違反規則,因為在"跨過"點時,點已經被"使用"過了。 技術分享圖片
現在工程師希望改進解鎖屏幕,增減點的數目,並移動點的位置,不再是一個九宮格形狀,但保持上述畫線的規則不變。 請計算新的解鎖屏幕上,一共有多少滿足規則的畫線方案。

Input

輸入文件第一行,為一個整數n,表示點的數目。 接下來n行,每行兩個空格分開的整數xi和yi,表示每個點的坐標。 -1000≤xi,Yi≤l000,1≤n<20。各點坐標不相同

Output

輸出文件共一行,為題目所求方案數除以100000007的余數。

Sample Input

4
0 0
1 1
2 2
3 3

Sample Output

8
解釋:設4個點編號為1到4,方案有1→2→3→4,2→1→3→4,3→2→1→4,2→3→1→4,
及其鏡像4→3→2→1,3→4→2→1,2→3→4→1,3→2→4→1.
一道挺簡單的狀壓DP……
不知道CQ省選為什麽會出狀壓板子題
先預處理出連接兩點會經過哪些點
然後f[i][S]表示以i結尾,當前已經選中的點狀態為S
從小到大枚舉S進行轉移
理論復雜度n^2*2^n,然而肯定跑不滿就是了。
還有把1e-16寫成-1e16這麽丟人的事我才不會說╭(╯^╰)╮
 1 #include<iostream>
 2
#include<cstring> 3 #include<cstdio> 4 #include<cmath> 5 #define N (600000) 6 using namespace std; 7 int n,x[N],y[N],f[21][N],line[21][21],ans,num[N]; 8 9 double K(double x1,double y1,double x2,double y2) 10 { 11 if (x1==x2) return 10001; 12 if (y1==y2) return 0; 13 return (y2-y1)/(x2-x1); 14 } 15 16 int main() 17 { 18 scanf("%d",&n); 19 for (int i=1; i<=n; ++i) 20 scanf("%d%d",&x[i],&y[i]); 21 22 for (int i=1; i<=n-1; ++i) 23 for (int j=i+1; j<=n; ++j) 24 { 25 line[i][j]=line[j][i]=(1<<i-1)|(1<<j-1); 26 for (int k=1; k<=n; ++k) 27 { 28 if (abs(K(x[k],y[k],x[i],y[i])-K(x[j],y[j],x[i],y[i]))>1e-16) continue; 29 if (!( x[k]>=min(x[i],x[j]) && x[k]<=max(x[i],x[j]) )) continue; 30 if (!( y[k]>=min(y[i],y[j]) && y[k]<=max(y[i],y[j]) )) continue; 31 line[i][j]|=(1<<k-1), line[j][i]=line[i][j]; 32 } 33 } 34 35 for (int i=1; i<=n; ++i) 36 f[i][1<<i-1]=1; 37 for (int i=1; i<=(1<<n)-1; ++i) 38 for (int j=1; j<=n; ++j) 39 if (i&(1<<j-1)) 40 for (int k=1; k<=n; ++k) 41 if (!(i&(1<<k-1)) && (((i|line[j][k])^(1<<k-1)))==i) 42 (f[k][i|(1<<k-1)]+=f[j][i])%=100000007; 43 44 for (int i=1; i<=(1<<n)-1; ++i) 45 { 46 int x=i,cnt=0; 47 for (int j=1; j<=n; ++j){if (x&1) cnt++; x>>=1;} 48 if (cnt<4) continue; 49 for (int j=1; j<=n; ++j) 50 (ans+=f[j][i])%=100000007; 51 } 52 printf("%d",ans); 53 }

5299. [CQOI2018]解鎖屏幕【狀壓DP】