1. 程式人生 > >【BZOJ5299】【CQOI2018】解鎖屏幕(動態規劃,狀態壓縮)

【BZOJ5299】【CQOI2018】解鎖屏幕(動態規劃,狀態壓縮)

++ src 規劃 希望 getch cstring online androi 形狀

【BZOJ5299】【CQOI2018】解鎖屏幕(動態規劃,狀態壓縮)

題面

BZOJ
洛谷

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\),坑的死
首先好好觀察一下模數,它TM不是\(10^9+7\)
。。。
然後考慮狀壓
\(f[i][j]\)表示當前狀態是\(i\),當前位置結尾的點是\(j\)的方案數
暴力枚舉一下個連接誰,檢查它們中間的點是否都已經被選擇
如果每次都\(check\)一下,復雜度就會變成\(O(2^nn^3)\)
提前預處理出任意兩個點之間的集合,這一步的復雜度是\(O(n^3)\)


計算是否貢獻的時候,不要計算出斜率再計算,這樣會掉精度
把斜率乘到兩邊就行了。
然後做\(dp\)的時候就可以\(O(1)\)檢查了
這樣復雜度就是\(O(n^3+2^nn^2)\),並且不滿。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define MOD 100000007
inline int read()
{
    RG int x=0,t=1;RG char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
int n,X[20],Y[20];
int g[20][20];
int f[20][1<<20];
void update(int &x,int y){x+=y;if(x>=MOD)x-=MOD;}
int main()
{
    n=read();
    for(int i=0;i<n;++i)X[i]=read(),Y[i]=read();
    for(RG int i=0;i<n;++i)
        for(RG int j=0;j<n;++j)
        {
            if(i==j)continue;
            for(RG int k=0;k<n;++k)
            {
                if(k==i||k==j)continue;
                if(X[k]<X[i]&&X[k]<X[j])continue;
                if(X[k]>X[i]&&X[k]>X[j])continue;
                if(Y[k]<Y[i]&&Y[k]<Y[j])continue;
                if(Y[k]>Y[i]&&Y[k]>Y[j])continue;
                if(Y[i]==Y[k])
                {
                    if(Y[j]==Y[k])g[i][j]|=(1<<k);
                    continue;
                }
                if(Y[j]==Y[k])continue;
                if((X[i]-X[k])*(Y[j]-Y[k])==(X[j]-X[k])*(Y[i]-Y[k]))g[i][j]|=(1<<k);
            }
        }
    for(RG int i=0;i<n;++i)f[i][1<<i]=1;
    for(RG int t=0;t<(1<<n);++t)
        for(RG int i=0;i<n;++i)
        {
            if(!(t&(1<<i)))continue;
            if(!f[i][t])continue;
            for(RG int j=0;j<n;++j)
            {
                if(t&(1<<j))continue;
                if((g[i][j]&t)!=g[i][j])continue;
                update(f[j][t|(1<<j)],f[i][t]);
            }
        }
    int ans=0;
    for(RG int t=0;t<(1<<n);++t)
    {
        int tot=0;
        for(RG int i=t;i;i-=i&(-i))++tot;
        if(tot<4)continue;
        for(RG int i=0;i<n;++i)
            if(t&(1<<i))update(ans,f[i][t]);
    }
    printf("%d\n",ans);
    return 0;
}

【BZOJ5299】【CQOI2018】解鎖屏幕(動態規劃,狀態壓縮)