交互题解题报告

交互格式

交互题一般分为两种:IO交互grader交互

IO交互

需要包含对应的交互库头文件。
一般题目会在题面定义好每种操作的格式,选手通过打印对应的字符串进行交互。
每次输出后要即时清空缓存,保证交互正常进行。
对于cout,endl时会自动清除缓存。
对于printf,可以使用 fflush(stdout) 清除缓存。
例题:猜数字2

grader交互

需要包含对应的交互库头文件。
题目一般会给出几个函数,有一些是交互库中已经写好的,一些是需要自己实现的,选手不需要定义已经写好的代码,只需要按照题目格式定义并编写要求选手实现的代码即可。
不应该包含main函数。
例题:拉面比较

其他注意事项

如果OJ或者题目有特殊要求,优先遵循其要求。如:luogu要求交互题必须不包含交互库头文件,对于grader交互题必须对所有给出函数进行定义。

交互题的本地测试

准备

题目一般会给出用于交互评测的代码以及交互库头文件,需提前下载好。

IO交互测试

貌似只能本地模拟。

grader交互测试

这里环境为linux。
假设交互评测的代码为grader.cpp,选手的代码为U1.cpp
进入终端输入命令g++ -O2 -o work grader.cpp U1.cpp,其中work为最终生成的程序的名字,可以自行定义。
./work运行程序,按照评测代码的输入格式输入数据,即可进行交互并打印评测结果。

题解

猜数字2

题目

交互的模板题,二分猜即可。
注意交互格式,及时清除缓存。

参考代码:

#include<bits/stdc++.h>
#include"interaction.h"
using namespace std;
const int N=110;
int n,a[N];
int guess(int R){
   
    int l=1,r=1e6,mid,ans=0,tmp;
    while(l+1<r){
   
        mid=(l+r)>>1;
        printf("guess %d %d\n",R,mid);
        fflush(stdout);
        scanf("%d",&tmp);
        if(tmp==0) return mid;
        else if(tmp==-1) l=mid;
        else r=mid;
    }
    printf("guess %d %d\n",R,l);
    fflush(stdout);
    scanf("%d",&tmp);
    if(tmp==0) return l;
    return r;
}
int main(){
   
//    freopen(".in","r",stdin);
//    freopen(".out","w",stdout);
    printf("get_num\n");
    fflush(stdout);
    scanf("%d",&n);
    for(int i=0;i<n;i++) a[i]=guess(i);
    printf("submit");
    for(int i=0;i<n;i++) printf(" %d",a[i]);
    printf("\n");
    return 0;
}
拉面比较
20pts: N ≤ 30 N\leq 30 N30

直接进行两两比较,比较次数上限为 n ( n − 1 ) 2 \frac{n(n-1)}{2} 2n(n1)

50pts: N ≤ 300 N\leq 300 N300

扫一遍序列,并记录前 i i i个元素的最大值和最小值的位置,每次只和最大值/最小值比较即可。
比较次数上限为 2 n − 2 2n-2 2n2

100pts: N ≤ 400 N\leq 400 N400

先每两个元素分成一组,进行 ⌊ n 2 ⌋ \lfloor\frac{n}{2}\rfloor 2n
次比较,将较小数分到一个集合 A A A,较大数分到几个集合 B B B,运用50pts的算法对 A A A求最小值,对 B B B求最大值,两个过程各需 ⌈ n 2 ⌉ − 1 \lceil\frac{n}{2}\rceil-1 2n1次比较。
比较次数上限为 ⌈ 3 n 2 ⌉ − 2 \lceil\frac{3n}{2}\rceil-2 23n2

参考代码:

#include<bits/stdc++.h>
#include"ramen.h"
using namespace std;
vector<int> biger,smler;
//int Compare(int X,int Y);
//int Answer(int X,int Y);
void Ramen(int n){
   
    if(n==1){
   Answer(0,0);return;}
    for(int i=1;i<n;i+=2){
   
        int tmp=Compare(i-1,i);
        if(tmp==1) biger.push_back(i-1),smler.push_back(i);
        else biger.push_back(i),smler.push_back(i-1);
    }
    if(n&1) biger.push_back(n-1),smler.push_back(n-1);
    int mx=biger[0];
    for(int i=1;i<biger.size();i++){
   
        if(Compare(mx,biger[i])<0) mx=biger[i];
    }
    int mn=smler[0];
    for(int i=1;i<smler.size();i++){
   
        if(Compare(mn,smler[i])>0) mn=smler[i];
    }
    Answer(mn,mx);
}
//int main(){
   
//    freopen(".in","r",stdin);
//    freopen(".out","w",stdout);
//
//    return 0;
//}
「JOISC 2017 Day 3」自然公园

题目

task1 10pts: N ≤ 250 N\leq 250 N250

暴力枚举两个点,检查能否直接从一个点到另一个点,有则两点存在连边。

namespace task1{
   
    void sol(){
   
        for(int i=0;i<n;i++)
            for(int j=i+1;j<n;j++){
   
                for(int k=0;k<n;k++){
   
                    if(k==i||k==j) pla[k]=1;
                    else pla[k]=0;
                }
                if(Ask(i,j,pla)) Answer(i,j);
            }
    }
}
task2 10pts: M = N − 1 M=N-1 M=N1,图是一条链,且 0 0 0 N − 1 N-1 N1为端点。

考虑整体二分。
每次递归维护一个序列,左右端点固定,从中间随机一个点作为分界点。
先将序列里所有点置成 1 1 1,从左到右枚举点,尝试将该点状态设成0,检查 Ask(l,mid) 是否为真,若为真说明该点在右区间,否则在左区间。

namespace task2{
   
    void Div(vector<int> vec){
   
        if(vec.size()==2) return report(vec[0],vec[1]),void();
        int l=vec[0],r=vec.back(),mid=vec[rnd()%(vec.size()-2)+1];
        vector<int> L,R;L.push_back(l);R.push_back(mid);
        for(int p:vec) pla[p]=1;
        for(int i=1;i<(int)vec.size()-1;i++){
   
            if(vec[i]==mid) continue;
            pla[vec[i]]=0;
            if(qry(l,mid,pla)) R.push_back(vec[i]);
            else L.push_back(vec[i]);
            pla[vec[i]]=1;
        }
        L.push_back(mid);R.push_back(r);
        for(int p:vec) pla[p]=0;
        Div(L);Div(R);
    }
    void sol(){
   
        vector<int> vec;
        for(int i=0;i<n;i++) vec.push_back(i);
        shuffle(vec.begin()+1,vec.end()-1,rnd);//at least two points,don't worry about RE
        Div(vec);
    }
}
task3 27pts: M = N − 1 M=N-1 M=N1,图是树,深度 l o g log log级别

考虑以 0 0 0为根,从上到下一层一层拓展,找到每个点的父亲,各自与其父亲连边。
对于每一层拓展,维护上一次新拓展的一层点(该层记为 Q Q Q),对于当前考虑要拓展的一层 A A A,进行整体二分。在 Q Q Q中取 m i d mid mid m i d mid mid左侧置1, m i d mid mid右侧置0,检查每一个在 A A A的点 i i i,若 Ask(0,i) 为真,说明该点父亲在左侧区间,否则在右侧区间。
每一轮整体二分完毕后,将 Q Q Q永久置为 1 1 1,将 A A A作为新的 Q Q Q。将 Q Q Q置为1,枚举剩余未拓展的点,若该点 Ask(0,i) 为真,则该点在 A A A层。找到所有 A A A层点后继续新一轮整体二分。

namespace task3{
   
    int pre[N],stk[N],fa[N],lq[N],rq[N],cnt=0,pretop=0;bool vis[N];
    void Div(int l,int r,int ql,int qr){
   
        if(l>r||ql>qr) return ;
        if(l==r){
   
            for(int i=ql;i<=qr;i++) fa[stk[i]]=pre[l];
            return ;
        }
        int mid=(l+r)>>1,lt=0,rt=0;
        for(int i=l;i<=mid;i++) pla[pre[i]]=1;
        for(int i=mid+1;i<=r<
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值