CQOI 2018 解鎖屏幕

洛谷 P4460 [CQOI2018]解鎖屏幕

洛谷傳送門node

題目背景

使用過Android 手機的同窗必定對手勢解鎖屏幕不陌生。Android 的解鎖屏幕由3X3 個點組成,手指在屏幕上畫一條線,將其中一些點鏈接起來,便可構成一個解鎖圖案。以下面三個例子所示:spa

img img img

題目描述

畫線時還須要遵循一些規則:code

  1. 鏈接的點數不能少於4 個。也就是說只鏈接兩個點或者三個點會提示錯誤。
  2. 兩個點之間的連線不能彎曲。
  3. 每一個點只能「使用」一次,不可重複。這裏的「使用」是指手指劃過一個點,該點變綠。
  4. 兩個點之間的連線不能「跨過」另外一個點,除非那個點以前已經被「使用」過了。

對於最後一條規則,參見下圖的解釋。左邊兩幅圖違反了該規則; 而右邊兩幅圖(分別爲2->4-1-3-6 和6->5-4->1->9-2) 則沒有違反規則,由於在「跨過」點時,點已經被「使用」過了。對象

img

如今工程師但願改進解鎖屏幕,增減點的數目,並移動點的位置,再也不是一個九宮格形狀,但保持上述畫線的規則不變。請計算新的解鎖屏幕上,一共有多少知足規則的畫線方案。blog

輸入格式

輸入文件第一行,爲一個整數n,表示點的數目。get

接下來n 行,每行兩個空格分開的整數x_i*x**i* 和y_i*y**i*,表示每一個點的座標。it

輸出格式

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

輸入輸出樣例

輸入 #1複製class

輸出 #1複製搜索

輸入 #2複製

輸出 #2複製

說明/提示

樣例#1解釋: 設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。

對於30%的數據,1≤n≤101≤n≤10

對於100%的數據,-1000≤x_i,y_i≤1000,1≤n<20−1000≤xi,yi≤1000,1≤n<20。各點座標不相同

題解:

七哥@littleseven 推薦的題。必需要作

看到方案數想到遞推,而後\(1\le N\le 20\)。因此想到狀態壓縮。而後七哥讓我推狀態。我一想,若是開多維的話可能空間會爆,因此考慮只開一維,壓成一個\(1<<n\)的狀態,表示每一個點是否被鏈接。

後來發現無法轉移。因此考慮再開一維,因而處處搜索...拿什麼作第二維能保證既能轉移又不會掛空間呢?

一看數據範圍,只有點數的二十還符合第二點要求,因此就選擇這個作狀態。苦思冥想看完題解以後,以爲把狀態設置成這樣最合適:

\(dp[i][j]\)表示點的選擇情況爲\(i\)、選擇的點集中終點爲\(j\)時的方案數。

那麼,針對於一個已定的狀態\(i\),它能轉移的對象是這個狀態中不爲1的點。

舉例:

\(dp[10010][2]\)能夠轉移到\(dp[11010][4],dp[10011][1]\)等狀態。咱們在轉移的時候判一下這個點可不能夠轉移便可。(條件是轉移前的\(j\)和轉移後的\(j\)連上的直線上全部的點所有已經被到達過)

這樣的話,咱們能夠先把兩點間的點開\(vector\)存下,而後進行遞推。

發現只連一個點的方案數是1.這個當初值。

咱們轉移的時候記錄把當前狀態用\(st\)記錄下來,而後再用\(k\)枚舉上一層狀態,若是符合條件(即上面的點都被通過了,用\(flag\)標記判斷進行轉移就好)

轉移方程是:
\[ dp[st][j]=(dp[st][j]+dp[i][k])\quad(mod\,\,\,p) \]
完整代碼就是:

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int mod=1e8+7;
int n;
bool flag;
int dp[1<<20][21];
struct node
{
    int x,y;
}a[21];
bool cmp(node a,node b)
{
    if(a.x==b.x)
        return a.y<b.y;
    return a.x<b.x;
}
vector<int> vec[21][21];
bool check(node a,node b,node c)
{
    return (a.x-b.x)*(b.y-c.y)==(b.x-c.x)*(a.y-b.y)?1:0;
}
int lowbit(int x)
{
    int ret=0;
    while(x)
    {
        x-=(x&(-x));
        ret++;
    }
    return ret;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d%d",&a[i].x,&a[i].y);
    sort(a+1,a+n+1,cmp);
    for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
            for(int k=i+1;k<j;k++)
                if(check(a[i],a[k],a[j]))
                    vec[i][j].push_back(k);
    for(int i=1;i<=n;i++)
        dp[1<<(i-1)][i]=1;
    for(int i=0;i<(1<<n);i++)
        for(int j=1;j<=n;j++)
        {
            if((i>>(j-1))&1)
                continue;
            int st=(i|(1<<(j-1)));
            for(int k=1;k<=n;k++)
            {
                if(k==j)
                    continue;
                int xx=min(j,k);
                int yy=max(j,k);
                flag=0;
                for(int t=0;t<vec[xx][yy].size();t++)
                    if(!((i>>(vec[xx][yy][t]-1))&1))
                    {
                        flag=1;
                        break;
                    }
                if(flag)
                    continue;
                dp[st][j]=(dp[st][j]+dp[i][k])%mod;
            }
        }
    int ans=0;
    for(int i=0;i<(1<<n);i++)
    {
        if(lowbit(i)<4)
            continue;
        for(int j=1;j<=n;j++)
            ans=(ans+dp[i][j])%mod;
    }
    printf("%d",ans);
    return 0;
}
相關文章
相關標籤/搜索