交互题

交互题

前言

所以 \(221\) 同学为啥子要学交互题呢?

为了APIO2023不爆零(\(\texttt {APIO 38pts 2023.07.05 补档}\)

引入

交互题,顾名思义,要求你的程序与命题人的程序(通常称为交互库)进行互动,来解决问题

引用 \(Codeforces\) 的话

This is an interactive problem. Remember to flush your output while communicating with the testing program.** You may use fflush(stdout) in C++, system.out.flush() in Java, stdout.flush() in Python or flush(output) in Pascal to flush the output.

命题意图

  • 解决实际问题。你不能获得所有的信息,只能通过若干个给出的接口向交互库提出问题,来得到你需要的答案。
  • 强制在线。强制在线一般有两种方式,对输入数据进行加密(常为异或上次答案),或者进行交互。交互题的特点决定了你每次只能获取一次询问,并返回答案,才能获取下一次询问。
  • 国外信息学竞赛(例如 \(APIO\))的题目往往不用你实现整个程序,而是实现几个接口函数,这类题目与交互题类似。
  • 虽然这是道传统题,但我就是要出成交互,我爱交互!!这种出题人……这题出的很好,下次别出了,祝 \(TA\) 身体健康。

交互方式

一般交互题有俩种交互方式:

  • \(Grader\) 交互(函数式交互):给你提供几个函数接口,向交互库提问时调用;同时你需要实现几个函数接口,供评测时获取你的答案用
  • \(I/O\) 式交互:通过标准输出流向交互库提问或给出答案,从标准输入流读入输入信息和提问的回答。

接下来分别介绍这俩种交互方式

\(Grader\) 交互方式

题目会给你一个必须包含的头文件和几个函数接口,你需要实现另外要求的函数接口

一般来讲,好的题目描述会通过如下方式给出:(仅为举例,不一定有实际意义)

  • 请在程序中包含 array.h 头文件。
  • 你可以调用一个函数 int compare(int x, int y),返回值为:若 \(x \lt y\) ,返回 \(-1\) ;若 \(x \gt y\) ,返回 \(1\) (这个函数接口没啥意义,但是一个挺好的例子)
  • 你需要实现一个函数 void init(vector<int> A, int n),接收输入数据。这个函数会在程序开始时被调用恰好一次
  • 你需要实现一个函数 int find_minimum(int l, int r),返回值为 \(A\) 中下标在 \([l,r]\) 的数中最小数的值,这个函数会被调用不超过 \(q\)

此时你可以实现下面一个程序:

#include "array.h"
#include <bits/stdc++.h>
using namespace std;

void init(vector<int> A, int n) {
    ; // 什么都不做
}
int find_minimum(int l, int r) {// 这么做是没有意义的,只是一个例子
    if (compare(l, r)) return 0; // 当区间长度大于 1 时,返回 0
    return 1;                   // 当区间只有一个数时,返回 1
}

注意你不需要,也不应当实现主函数

在提交前仔细检查是否包含头文件、是否删掉了主函数

请注意这个例子与实际比赛中 Grader 交互题的不同:因为洛谷交互题实现原因,这道题没有要求包含头文件,而实际比赛中往往需要

I/O 式交互

这类交互在实际比赛中较少用到(除了 \(CodeForces\) 等线上平台的比赛),建议重点掌握 \(Grader\) 交互,这种也要会

题目会给你你的询问方式,并给出你答题的方式

举一个猜数的例子:

  • 首先你要读入一个整数 \(n\) ,表示待猜的数在 \([1,n]\) 中。
  • 你可以通过 ? x 的方式向交互库提问,随后读入一个整数表示回答。如果 \(x\) 小于待猜的数,交互库回答 \(−1\) ;如果 \(x\) 等于待猜的数,交互库回答 \(0\) ;如果 \(x\) 大于待猜的数,交互库回答 \(1\)
  • 当你猜出这个数后,你可以通过 ! x 给出答案,\(x\) 即为这个数

注意 I/O 交互中你每次输出需要刷新输出缓冲区,对于 \(C/C++\) 语言可以 fflush(stdout); 对于 \(C++\) 语言可以 cout<<endl;

你可以实现这样的程序:

#include <bits/stdc++.h>
using namespace std;

signed main() {
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        printf("? %d\n", i);
        fflush(stdout);
        int x;
        scanf("%d", &x);
        if (!x) {
            printf("! %d\n", i);
            return 0;
        }
    }
    return 0;
}

当然这个程序会超时,只是作为例子我懒得写二分

关于调试

比赛时如果出题人给出了 \(grader\) 头文件(用于 \(grader\) 交互题的调试)或者 \(checker\) 程序(用于 \(I/O\) 交互题的调试),则交互题的调试比较简单,因为交互题的对拍会比普通题目的对拍困难很多。没有 testlib.h 的情况下。交互细节较多的题目的 stdio 交互库会一般有 \(3k\) 代码量,再加上 \(3k\) 长度的对拍器,至少需要一小时实现。但是,无论是否有调试程序,调试交互题的代码都往往需要选手模拟与程序的交互过程,因此交互题需要选手能设计出高质量的程序,尽量保证一遍做对,同时拥有较强的静态查错能力。

posted @ 2023-08-05 11:33  Furthe77oad  阅读(81)  评论(2编辑  收藏  举报