牛客 - 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 进行转移,那么对于任意一个位置来说,只会有三种情况:

  1. x = 0 , y = 0,当前位置之前就被删除
  2. x = 0 , y = 1,本轮操作删除当前位置
  3. 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");
    else
        puts("fzj");
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_45458915/article/details/106799298