D. Strange Device

落爺英雄遲暮 提交于 2020-01-14 07:58:14

This problem is interactive.

We have hidden an array a of n pairwise different numbers (this means that no two numbers are equal). You can get some information about this array using a new device you just ordered on Amazon.

This device can answer queries of the following form: in response to the positions of k different elements of the array, it will return the position and value of the m-th among them in the ascending order.

Unfortunately, the instruction for the device was lost during delivery. However, you remember k, but don’t remember m. Your task is to find m using queries to this device.

You can ask not more than n queries.

Note that the array a and number m are fixed before the start of the interaction and don’t depend on your queries. In other words, interactor is not adaptive.

Note that you don’t have to minimize the number of queries, and you don’t need to guess array a. You just have to guess m.

Input
The first line contains two integers n and k (1≤k<n≤500) — the length of the array and the number of the elements in the query.

It is guaranteed that number m satisfies 1≤m≤k, elements a1,a2,…,an of the array satisfy 0≤ai≤10^9, and all of them are different.

Interaction
You begin the interaction by reading n and k.

To ask a question about elements on positions x1,x2,…,xk, in a separate line output

? x1 x2 x3 ... xk

Numbers in the query have to satisfy 1≤xi≤n, and all xi have to be different. Don’t forget to ‘flush’, to get the answer.

In response, you will receive two integers pos and apos — the position in the array a of the m-th in ascending order element among ax1,ax2,…,axk, and the element on this position.

In case your query is invalid or you asked more than n queries, the program will print −1 and will finish interaction. You will receive a Wrong answer verdict. Make sure to exit immediately to avoid getting other verdicts.

When you determine m, output

! m

After printing a query do not forget to output end of line and flush the output. Otherwise, you will get Idleness limit exceeded. To do this, use:

fflush(stdout) or cout.flush() in C++;
System.out.flush() in Java;
flush(output) in Pascal;
stdout.flush() in Python;
see documentation for other languages.
Hack format

For the hacks use the following format:

The first line has to contain three integers n,k,m (1≤m≤k<n≤500) — the length of the array, number of elements in the query, and which in the ascending order number the device returns.

In the next line output n integers a1,a2,…,an (0≤ai≤10^9) — the elements of the array. They have to be pairwise different.

Example

input
4 3
4 9
4 9
4 9
1 2
output
? 2 3 4
? 1 3 4
? 1 2 4
? 1 2 3
! 3

Note
In the example, n=4, k=3, m=3, a=[2,0,1,9].
翻译:
这个问题是交互式的。
我们隐藏了一个由n个不同的数字组成的数组a(这意味着没有两个数字是相等的)。您可以使用您刚才在亚马逊上订购的新设备来获得关于这个数组的一些信息。
该设备可以回答如下形式的查询:根据数组中k个不同元素的位置,按升序返回其中第m个元素的位置和值。
不幸的是,设备的指令在交付过程中丢失了。但是,你记得k,不记得m。你的任务是通过查询这个设备来找到m。
您最多可以查询n次。
注意,在开始交互之前,数组a和数字m是固定的,不依赖于查询。换句话说,交互器不是自适应的。
注意,您不需要最小化查询的数量,也不需要猜测数组a,只需猜测m。
输入
第一行包含两个整数n和k(1≤k<n≤500)——数组的长度和查询中的元素数量。
保证数字m满足1≤m≤k,数组的元素a1、a2、…、an满足0≤ai≤10^9,且各元素不同。
交互
你从读取到n和k开始互动。
要在单独的行输出中询问关于位置x1、x2、…、xk上的元素的问题

? x1 x2 x3…xk

查询中的数字必须满足1≤xi≤n,且所有xi必须不同。别忘了使用‘flush’来获取答案。
作为响应,您将获得两个整数pos和apos——ax1、ax2、…、axk的升序列中第m个元素在数组a中的位置,以及这个位置上的元素。
如果您的查询是无效的,或您要求超过n个查询,程序将打印- 1,并将完成交互。你会收到一个错误的答案。确保立即退出,以避免得到其他结果。
当你确定m时,输出

! m

思路:首先进行如下操作:
①询问1到n中的k个位置,得到第m个位置。
②步骤①中得到过的位置不在询问的序列中继续重复步骤①
这一循环最多能重复n-k+1次,得到n-k+1个返回值。
可以证明,其中最小的返回值对应的在序列a中第m小。
因为无论如何询问,序列a中总是有k-1个值是无法得到的,而这些值在a的升序列中的序号要么小于m,要么大于n-k+m。又因为通过步骤①②得到了n-k+1个不同的值,那么这n-k+1个值即为通过询问所能找到的所有值。因此其中最小的值是a序列中第m小的。
记这个最小值是x,得到这个最小值x的询问序列为k。不难得出,这个序列k中只能有m-1个数比x小,那么接下来的步骤就是寻找k中有几个值比x小,再将结果加1就是最终答案。这时,我们需要从序列a中找到一个不属于序列k的值tmp,那么这个tmp一定比x要大。用tmp置换序列k中除x之外的值,并用这个序列进行询问,若得到的值大于x,说明序列中有一个比x小的数被一个比x大的数换掉了;若得到的值仍为x,则比x大的数被换了。统计结果不相等的次数,加上1就是m的值。上述操作恰好进行了n次。

#include <bits/stdc++.h>

using namespace std;
const int MAX_N = 5e2 + 5;
int n, m, k;
int a[MAX_N];
bool mark[MAX_N];
vector<int> know, t;
map<int, vector<int>> rec;

void query1() {
    printf("? ");
    for (int i = 0; i <= k - 1; i++)
        printf("%d%c", t[i], i == k - 1 ? '\n' : ' ');
    fflush(stdout);
    int pos, val;
    scanf("%d%d", &pos, &val);
    a[pos] = val;//添加至序列a中
    rec[val] = t;//每一次询问出的值对应的询问序列
    know.push_back(val);//询问出的值
}

void query2() {
    printf("? ");
    for (int i = 0; i <= k - 1; i++)
        printf("%d%c", t[i], i == k - 1 ? '\n' : ' ');
    fflush(stdout);
    int pos, val;
    scanf("%d%d", &pos, &val);
    if (val != know[0])
        m++;
}

int main() {
    memset(a, -1, sizeof(a));
    cin >> n >> k;
    for (int i = 1; i <= n - k + 1; i++) {
        t.clear();
        int cnt = 0;
        for (int j = 1; j <= n; j++)
            if (a[j] == -1) {
                t.push_back(j);
                cnt++;
                if (cnt == k)
                    break;
            }
        query1();
    }
    sort(know.begin(), know.end());
    t = rec[know[0]];
    for (int i = 0; i <= k - 1; i++)
        mark[t[i]] = true;
    int tmp;
    for (int i = 1; i <= n; i++)
        if (!mark[i]) {
            tmp = i;
            break;
        }
    for (int i = 0; i <= k - 1; i++) {
        t = rec[know[0]];
        if (a[t[i]] == know[0])
            continue;
        t[i] = tmp;
        query2();
    }
    cout << "! " << m + 1 << endl;
    return 0;
}

看了一下dalao的程序

#include<iostream>
#include<map>

using namespace std;
map<int, int> mp;
int n, k, pos, x;

int main() {
    cin >> n >> k;
    for (int i = 1; i <= k + 1; i++) {
        cout << "? ";
        for (int j = 1; j <= k + 1; j++) {
            if (j != i)
                cout << j << " ";
        }
        cout << '\n';
        cin >> pos >> x;
        mp[x]++;
    }
    cout << "! " << k + 1 - mp.begin()->second;
}

直接在k+1个序列分别去除其中每一个元素,根据第m个数的变化直接得到m。
蒟蒻的我还要努力啊。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!