BJOI2018 双人猜数游戏

传送门:https://www.luogu.org/problemnew/show/P4459

提答题啊。。题目真骚。我们试着分析一下

现在假设ALICE开始

ALICE:不知道。。

现在BOB得到哪些信息了呢?

BOB知道在

有至少两个整数能被nm整除,不然ALICE咋会说不确定呢。。。

假设下一步BOB:不资道~~~

现在我们又得到哪些信息了呢?

BOB知道两数之和,那么BOB可以列举出一些可能的答案

BOB既然不资道那么在这些可能的答案中有至少两个满足ALICE的条件

好像dp啊。。。

我们假设表示Alice开始后的第p个询问之后alice和bob都回答不资道(Alice算第一个)i,j是否满足

若p==1 直接判断在

中是否只有一个整数被i*j整除是的话f(p,i,j)=0;否则f(p,i,j)=1;

如果f(p-1,i,j)等于0那么f(p,i,j)=0;

若p%2==0

列举出所有和等于i+j 且数大于s的情况判断这些答案满足的个数大于1可以,否则不行

若p%2==1

列举所有乘积等于i*j且数大于s的情况判断这些答案满足的个数大于1可以,否则不行

我们从1开始模拟他们询问与回答

如果我们在算到其中一个人的时候他的答案变唯一了那么ta知道了

接下来我们要看对方是否也会知道

我们把对方剩余的可能解进行分解,看答案为一的个数是否为1

如果是,那么他也知道了,如果不知道,那么他永远不会知道。。

以上是ALICE开始的情况BOB开始的话请自行列举~~

提答题,,所以我写了四个程序(用文件操作链接)。跑起来好慢。。25个数据跑完要0.5h

ALICE开始,模拟两人对答过程:

#include <bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(i=j;i<=k;++i)
#define dow(i,j,k) for(i=j;i>=k;--i)
const int N=500;
int s,n,m,dp[N+10][N+10][N+10];
bool f(int p,int x,int y){
    if(dp[p][x][y]!=-1)return dp[p][x][y];
    if(p==1){
        int i=s,cnt=0;
        for(;i*i<=x*y;++i)
        if(x*y%i==0)++cnt;
        return dp[p][x][y]=(cnt>1);
    }if(f(p-1,x,y)==0)return dp[p][x][y]=dp[p-1][x][y];
    if(p%2==0){
        int i,cnt=0;
        rep(i,s,(x+y>>1))
        if(f(p-1,i,x+y-i))++cnt;
        return dp[p][x][y]=(cnt>1);
    }else{
        int i,cnt=0;
        for(i=s;i*i<=x*y;++i)if(x*y%i==0 && f(p-1,i,x*y/i))++cnt;
        return dp[p][x][y]=(cnt>1);
    }
}
int main(){freopen("in.txt","r",stdin);freopen("o.txt","w",stdout);
    scanf("%d%d%d",&s,&m,&n);
    int i=1,j,k,ct=0;rep(i,1,N)rep(j,1,N)rep(k,1,N)dp[i][j][k]=-1;
    for(i=s;i*i<=m*n;++i)if(m*n%i==0)++ct;
    if(ct==1){
        printf("0");return 0;
    }
    i=1;
    while(i<=500){
        if(i%2==0){
            int j,cnt=0;
            for(j=s;j*j<=n*m;++j)if(n*m%j==0 && f(i,j,n*m/j))++cnt;
            if(cnt==1){
            int c2=0;
                rep(j,s,(n+m)>>1){
                    int c3=0,jj;
                    for(jj=s;jj*jj<=j*(n+m-j);++jj)
                    if(j*(n+m-j)%jj==0 &&f(i,jj,j*(n+m-j)/jj))++c3;
                    c2+=(c3==1);
                }
                if(c2==1)
                printf("%d",i);else printf("-1");
                return 0;
            }
        }else{
            int j,cnt=0;
            rep(j,s,(n+m>>1))if(f(i,j,n+m-j))++cnt;
            if(cnt==1){
                            int c2=0;
                for(j=s;j*j<=n*m;++j)if(n*m%j==0){
                    int jj,c3=0;
                    rep(jj,s,(j+n*m/j)/2)if(f(i,jj,j+n*m/j-jj))++c3;
                    c2+=(c3==1);
                }
                if(c2==1)
                printf("%d",i);
                else printf("-1");
                return 0;
            }
        }
        ++i;
    }
}

BOB开始,模拟两人对答过程:

#include <bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(i=j;i<=k;++i)
#define dow(i,j,k) for(i=j;i>=k;--i)
const int N=500;
int s,n,m,dp[N+10][N+10][N+10];
bool f(int p,int x,int y){
    if(dp[p][x][y]!=-1)return dp[p][x][y];
    if(p==1){
        int i=s,cnt=0;
        for(;i+i<=x+y;++i)
        ++cnt;
        return dp[p][x][y]=cnt>1;
    }if(f(p-1,x,y)==0)return dp[p][x][y]=0;
    if(p%2==1){
        int i,cnt=0;
        rep(i,s,(x+y>>1))
        if(f(p-1,i,x+y-i))++cnt;
        return dp[p][x][y]=cnt>1;
    }else{
        int i,cnt=0;
        for(i=s;i*i<=x*y;++i)if(x*y%i==0 && f(p-1,i,x*y/i))++cnt;
        return dp[p][x][y]=cnt>1;
    }
}
int main(){
    freopen("in.txt","r",stdin);freopen("o.txt","w",stdout);
    scanf("%d%d%d",&s,&m,&n);
    int i=1,j,k;rep(i,1,N)rep(j,1,N)rep(k,1,N)dp[i][j][k]=-1;
    i=1;
    if(m==n && m==s){
        printf("0");return 0;
    }
    while(i<=500){
        if(i%2==1){
            int j,cnt=0;
            for(j=s;j*j<=n*m;++j)if(n*m%j==0 && f(i,j,n*m/j))++cnt;
            if(cnt==1){
            int c2=0;
                rep(j,s,(n+m)>>1){
                    int c3=0,jj;
                    for(jj=s;jj*jj<=j*(n+m-j);++jj)if(j*(n+m-j)%jj==0 &&f(i,jj,j*(n+m-j)/jj))++c3;
                    c2+=(c3==1);
                }
                if(c2==1)
                printf("%d",i);else printf("-1");
                return 0;
            }
        }else{
            int j,cnt=0;
            rep(j,s,(n+m>>1))if(f(i,j,n+m-j))++cnt;
            if(cnt==1){
                int c2=0;
                for(j=s;j*j<=n*m;++j)if(n*m%j==0){
                    int jj,c3=0;
                    rep(jj,s,(j+n*m/j)/2)if(f(i,jj,j+n*m/j-jj))++c3;
                    c2+=(c3==1);
                }
                if(c2==1)
                printf("%d",i);
                else printf("-1");
                return 0;
            }
        }
        ++i;
    }printf("-1");
}

计算ALICE开始的答案

#include <bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(i=j;i<=k;++i)
#define dow(i,j,k) for(i=j;i>=k;--i)
int s,t;
int main(){
    scanf("%d%d",&s,&t);
    int p,x,y;
    rep(p,(s+s),700){
            rep(x,s,(p>>1)){
            freopen("in.txt","w",stdout);
            printf("%d %d %d\n",s,x,p-x);
            fclose(stdout);
            system("a.exe");
            freopen("o.txt","r",stdin);
            scanf("%d",&y);
            fclose(stdin);
            if(y==t){
                freopen("ans.txt","w",stdout);
                printf("%d %d",x,p-x);
                return 0;
            }
        }
    }
}

计算BOB答案

#include <bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(i=j;i<=k;++i)
#define dow(i,j,k) for(i=j;i>=k;--i)
int s,t;
int main(){
    scanf("%d%d",&s,&t);
    int p,x,y;
    rep(p,(s+s),700){
            rep(x,s,(p>>1)){
            freopen("in.txt","w",stdout);
            printf("%d %d %d\n",s,x,p-x);
            fclose(stdout);
            system("b.exe");
            freopen("o.txt","r",stdin);
            scanf("%d",&y);
            fclose(stdin);
            if(y==t){
                freopen("ans.txt","w",stdout);
                printf("%d %d",x,p-x);
                return 0;
            }
        }
    }
}

 

转载于:https://www.cnblogs.com/david--lj/p/9503686.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值