[CQOI2018] 解锁屏幕(状压dp)
生活随笔
收集整理的這篇文章主要介紹了
[CQOI2018] 解锁屏幕(状压dp)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
problem
luogu-P4460
solution
題面以及數據告訴我們顯然是狀壓 dpdpdp。
設 f(s,i):f(s,i):f(s,i): 經過的點集 sss 最后一次畫的點為 iii 的方案數。
直接枚舉下一個之前沒被畫的點 jjj 轉移即可。
f(s∣2j,j)←f(s,i)f(s|2^j,j)\leftarrow f(s,i)f(s∣2j,j)←f(s,i)。
但這里需要保證 i,ji,ji,j 兩點間若存在點,必須這些點之前都被畫過了。
我們預處理,開個 bitset\text{bitset}bitset ,g(i,j):g(i,j):g(i,j): 與 i,ji,ji,j 貢獻且在 i,ji,ji,j 線段上的點集。
共線判斷我們常用的是斜率,即 yk?yixk?xi=yk?yjxk?xj\frac{y_k-y_i}{x_k-x_i}=\frac{y_k-y_j}{x_k-x_j}xk??xi?yk??yi??=xk??xj?yk??yj??。
但計算機 /0/0/0 是會 RE\text{RE}RE 的,所以我們盡量避免處罰,交叉相乘判斷相等即可。
共線只是一個條件,必須是在 i,ji,ji,j 形成的線段上,所以和 i,ji,ji,j 的橫縱坐標判斷一下即可。
最后的最后,就是這道題可以不用完所有點。
條件只說了畫的點數不小于 444 即可。
code
#include <bits/stdc++.h> using namespace std; #define int long long #define mod 100000007 #define maxn 20 bitset < maxn > g[maxn][maxn]; int n, ans; int X[maxn], Y[maxn], f[1 << maxn][maxn];bool check( int l, int r, int i ) {if( (X[i] - X[l]) * (Y[i] - Y[r]) != (X[i] - X[r]) * (Y[i] - Y[l]) )return 0;if( (X[i] >= max(X[l], X[r]) or X[i] <= min(X[l], X[r])) and (Y[i] >= max(Y[l], Y[r]) or Y[i] <= min(Y[l], Y[r])) ) return 0;return 1; }signed main() {scanf( "%lld", &n );for( int i = 0;i < n;i ++ ) scanf( "%lld %lld", &X[i], &Y[i] );if( n < 4 ) return ! puts("0");for( int i = 0;i < n;i ++ )for( int j = 0;j < n;j ++ )for( int k = 0;k < n;k ++ )if( i == j or i == k or j == k ) continue;else if( check( i, j, k ) ) g[i][j][k] = 1;for( int i = 0;i < n;i ++ ) f[1 << i][i] = 1;for( int s = 0;s < (1 << n);s ++ ) {for( int i = 0;i < n;i ++ )if( f[s][i] )for( int j = 0;j < n;j ++ )if( s >> j & 1 ) continue;else {for( int k = g[i][j]._Find_first();k != g[i][j].size();k = g[i][j]._Find_next( k ) )if( ! (s >> k & 1) ) goto pass;(f[s | (1 << j)][j] += f[s][i]) %= mod;pass:;}}int ans = 0;for( int s = 0;s < (1 << n);s ++ )if( __builtin_popcount( s ) >= 4 )for( int i = 0;i < n;i ++ )(ans += f[s][i]) %= mod;printf( "%lld\n", ans );return 0; }總結
以上是生活随笔為你收集整理的[CQOI2018] 解锁屏幕(状压dp)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [SDOI2019] 热闹的聚会与尴尬的
- 下一篇: [十二省联考 2019] 异或粽子(可持