蓝桥杯 历届试题 约数倍数选卡片 (经典数论+DFS)

闲暇时,福尔摩斯和华生玩一个游戏:
在N张卡片上写有N个整数。两人轮流拿走一张卡片。要求下一个人拿的数字一定是前一个人拿的数字的约数或倍数。例如,某次福尔摩斯拿走的卡片上写着数字“6”,则接下来华生可以拿的数字包括:
1,2,3, 6,12,18,24 ....
当轮到某一方拿卡片时,没有满足要求的卡片可选,则该方为输方。
请你利用计算机的优势计算一下,在已知所有卡片上的数字和可选哪些数字的条件下,怎样选择才能保证必胜!
当选多个数字都可以必胜时,输出其中最小的数字。如果无论如何都会输,则输出-1。

Input

  输入数据为2行。第一行是若干空格分开的整数(每个整数介于1~100间),表示当前剩余的所有卡片。
第二行也是若干空格分开的整数,表示可以选的数字。当然,第二行的数字必须完全包含在第一行的数字中。

Output

  程序则输出必胜的招法!!

Sample Input

样例输入1
2 3 6
3 6

样例输入2
1 2 2 3 3 4 5
3 4 5

Sample Output

样例输出1
3

样例输出2
4

Source

蓝桥杯
 
code:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define INF 99999999
#define me(a,x) memset(a,x,sizeof(a))
int mon1[13]= {0,31,28,31,30,31,30,31,31,30,31,30,31};
int mon2[13]= {0,31,29,31,30,31,30,31,31,30,31,30,31};
int dir[4][2]= {{0,1},{0,-1},{1,0},{-1,0}};
int fac[] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};//i的阶乘
LL getval()
{
    LL ret(0);
    char c;
    while((c=getchar())==' '||c=='\n'||c=='\r');
    ret=c-'0';
    while((c=getchar())!=' '&&c!='\n'&&c!='\r')
        ret=ret*10+c-'0';
    return ret;
}
void out(int a)
{
    if(a>9)
        out(a/10);
    putchar(a%10+'0');
}
int kt(int a[],int n)//康托展开
{
    int ans=0;
    for(int i=1; i<=n; i++) //下标从1开始
    {
        int c=0;
        for(int j=i+1; j<=n; j++)
        {
            if(a[j]<a[i])
                c++;
        }
        ans+=(c*fac[n-i]);
    }
    return ans+1;
}

#define max_v 105
int a[max_v];
vector<int> b;
vector<int> vv[max_v];

int dfs(int x)
{
    for(int i=vv[x].size()-1; i>=0; i--)//从大的数开始选,因为大的数约数和倍数少
    {
        int y=vv[x][i];
        if(a[y])//对手选择的数字存在
        {
            a[y]--;
            int flag=dfs(y);//判断对手是否为必胜态
            a[y]++;
            if(flag)//对手为必胜态,则我为必败态
                return 0;
        }
    }
    return 1;//对手没有必胜态,则我是必胜态
}

int main()
{
    int x;
    me(a,0);
    string str;
    getline(cin,str);
    stringstream ss(str);
    while(ss>>x) a[x]++;
    getline(cin,str);
    stringstream st(str);
    while(st>>x)
        b.push_back(x);

    sort(b.begin(),b.end());
    for(int i=1; i<=100; i++)
    {
        if(a[i]!=0)
        {
            for(int j=1; j<=100; j++)//得到每个i的倍数或者约数
            {
                if((i%j==0||j%i==0)&&a[j]!=0)
                    vv[i].push_back(j);
            }
        }
    }
    int flag=0;
    for(int i=0; i<b.size(); i++)
    {
        int x=b[i];//我先手选
        if(a[x])//选的数字存在
        {
            a[x]--;//选了一个
            if(dfs(x))//搜索判断我选择该数字是否为必胜态
            {
                printf("%d\n",x);
                flag=1;
                break;
            }
            a[x]++;//回退
        }
    }
    if(flag==0)
        printf("-1\n");
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值