Toyota Programming Contest 2024#1(AtCoder Beginner Contest 337)
E - Bad Juice
题目链接
题目:
这是一个交互题(一种和评测程序通过标准输入输出进行交互的问题)
有 N N N ( 2 ≤ N ≤ 100 ) (2\le N \le 100) (2≤N≤100)瓶果汁,编号从 1 1 1 到 N N N。有一瓶坏了,喝一小口第二天就会胃疼。
Takahashi必须在下一天辨别出哪一瓶是坏的。为此,他要叫最少的朋友来来喝他的果汁。他可以给每个朋友任意瓶任意编号的果汁。
输出要叫几个朋友和如何分发果汁。第二天收到朋友拉肚子的消息,并打印出坏掉的果汁编号。
交互:
在交互之前,坏果汁的编号会被秘密确定,不会告诉你,坏果汁的编号在交互过程中可以更改,不过一定会保证它不会和先前的输出冲突。
首先会给你一个数 N N N
紧接着一行你需要打印一个数 M M M,表示最小要叫几个朋友。
然后,你要执行接下来的过程,并输出
M
M
M 个结果:
对
i
=
1
,
2
,
…
,
M
i=1,2,…,M
i=1,2,…,M ,第
i
i
i 行输出包括你要给第
i
i
i 个朋友的饮料瓶数和每瓶饮料的编号,编号满足单调递增。
然后评测程序会给你一个长为
M
M
M 的01字符串
S
S
S ,对
i
=
1
,
2
,
…
,
M
i=1,2,…,M
i=1,2,…,M,
S
i
=
1
S_i=1
Si=1 表示第
i
i
i 个朋友闹肚子。
然后你需要打印坏果汁的编号,如果你给出的答案中朋友数确实是最小的,而且答案唯一,程序才被认为是正确的。
思路:
这又是哪家的面试题()
考虑模仿一下二分的思想,比如有8瓶果汁,假如从0开始编号,第一个朋友喝前一半,如果闹肚子了,说明坏果汁在前一半里,否则就在后一半里。对第二个朋友,由于0 ~ 3 和 4 ~ 7 的果汁不会相互影响,所以我们可以让他喝每一个的前一半,也就是0 1 和 4 5 ,这样范围就又缩小了一半。
找找规律,发现第一个朋友喝的果汁编号二进制第3位一定是0,第二个朋友喝的果汁编号第2位一定是0,第一个朋友喝的果汁编号第1位一定是0。其实也好理解,第 i i i 个朋友喝掉全部编号第 i i i 位为0的果汁,如果第 i i i 个朋友拉肚子了,二进制第 i i i 位这里一定是0,否则是1,一位一位确定。因此最少需要 M = ⌈ log 2 N ⌉ M=\lceil \log_2N \rceil M=⌈log2N⌉ 个朋友。
不过这里编号是从1开始的,所以算的时候用0 ~ N-1,输出的时候编号要加1。
之前看过一个很像的面试题,就是有n瓶水,一瓶有毒,问最少用几种小白鼠可以一次试出来哪瓶有毒。
code:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int maxn=105;
int n,x,t;
int a[maxn];
int main(){
cin>>n;
x=ceil(log2(n));
cout<<x<<endl;
for(int i=x-1;i>=0;i--){
a[0]=0;
for(int j=0;j<n;j++){
if(!(j&(1<<i)))//第i位为0
a[++a[0]]=j;
}
printf("%d ",a[0]);
for(int j=1;j<=a[0];j++)
cout<<a[j]+1<<" ";
cout<<endl;
}
string s;
cin>>s;
reverse(s.begin(),s.end());
int ans=0;
for(int i=x-1;i>=0;i--)
if(s[i]=='0')ans|=(1<<i);
cout<<ans+1<<endl;
return 0;
}