生活随笔
收集整理的這篇文章主要介紹了
牛客 - Dress as women(sg定理+位运算)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
題目鏈接:點擊查看
題目大意:兩個人正在玩游戲,現在給出 n 個點,兩個人輪流操作,每次可以選擇任意個貢獻的頂點然后刪除,最后無法操作的人失敗
題目分析:讀完題后想到了之前做過的一道題目:點擊查看
因為 n 比較小,不難想到狀態壓縮,所以最樸素的做法就是設 m = 2^n ,直接 m * m 枚舉狀態然后寫 dfs 轉移狀態就好了,在此之前需要記得預處理一下共線的限制,共線限制也可以利用狀態壓縮來保存,只需要判斷每個二進制狀態中所有為 1 的位置是否共線即可,在處理共線限制時,因為時間復雜度計算的比較充裕,所以我直接選擇了 2^n * n^3 來預處理(正解好像是 2^n *n 的時間復雜度預處理的),原理就是:如果某些頂點貢獻,那么任意三個頂點都共線,這樣 2^n 枚舉狀態,n^3 枚舉三個頂點判斷是否貢獻即可,這樣總的時間復雜度就是 4^n ,還好出題人沒有卡時間,1.2s 水過去了
但顯然這樣肯定不是正解,賽后補題時,仔細分析了一下,發現:
假設當前是狀態 x 向狀態 y 進行轉移,那么對于任意一個位置來說,只會有三種情況:
x = 0 , y = 0,當前位置之前就被刪除x = 0 , y = 1,本輪操作刪除當前位置x = 1 , y = 1,當前位置本輪也不刪除
注意到沒有,當 x = 1 且 y = 0 時的這個狀態是不合法的,所以我們完全可以通過搜索進行剪枝將時間復雜度降低到 3^n,這也正需要一個小知識點:如何枚舉子集的子集
當然只需要兩行代碼就可以實現
for (int S=1; S<(1<<n); S++)for (int S0=S;S0;S0=(S0-1)&S)
然后實現就非常簡單了
代碼:
?
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
using namespace std;typedef long long LL;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const int N=15;int n;LL x[N],y[N];bool can[(1<<N)+100];int dp[(1<<N)+100];void init()
{auto check=[&](int i,int j,int k){return (y[i]-y[j])*(x[i]-x[k])==(y[i]-y[k])*(x[i]-x[j]);};for(int t=1;t<1<<n;t++){can[t]=true;for(int i=0;i<n;i++){if((t&(1<<i))==0)continue;for(int j=i+1;j<n;j++){if((t&(1<<j))==0)continue;for(int k=j+1;k<n;k++){if((t&(1<<k))==0)continue;if(!check(i,j,k))can[t]=false;}} } }
}int main()
{
#ifndef ONLINE_JUDGE
// freopen("input.txt","r",stdin);
// freopen("output.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);scanf("%d",&n);for(int i=0;i<n;i++)scanf("%lld%lld",x+i,y+i);init();for(int i=0;i<1<<n;i++)for(int j=i;j;j=(j-1)&i)if(can[j]&&!dp[i^j])dp[i]=true;if(dp[(1<<n)-1])puts("zyh");elseputs("fzj");return 0;
}
?
總結
以上是生活随笔為你收集整理的牛客 - Dress as women(sg定理+位运算)的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。