题解 P6558 【[APIO2017] 考拉的游戏】
好van的交互题。。
Subtask 1
询问一次即可。
直接询问
理由很简单,Koala 至少得不到一个物品,那么不要的物品一定是价值最小的物品。
code:
int minValue(int n, int W) {
static int a[N],b[N];
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
a[0]=1;
playRound(a,b);
for(int i=0;i<n;++i){
if(b[i]<=a[i])return i;
}
return 114514; //真香
}
Subtask 2
这个 Subtask 不是很好想。
首先发现这个 Subtask 的套路一定是逐步缩小前若干大值位置的集合大小,直到集合大小为
下面就是拿交互库乱试了!
首先每个位置放一个
然后再把这个集合中的每个位置放
按照这个套路,之后分别放
刚好询问
code:
int maxValue(int n, int W) {
static int a[N],b[N];
memset(a,0,sizeof(a));
for(int i=0;i<n;++i){
a[i]=1;
}
playRound(a,b);
memset(a,0,sizeof(a));
for(int i=0;i<n;++i){
if(b[i]>1)a[i]=2;
}
playRound(a,b);
memset(a,0,sizeof(a));
for(int i=0;i<n;++i){
if(b[i]>2)a[i]=4;
}
playRound(a,b);
memset(a,0,sizeof(a));
for(int i=0;i<n;++i){
if(b[i]>4)a[i]=11;
}
playRound(a,b);
for(int i=0;i<n;++i){
if(b[i]>11)return i;
}
return 1919810; //越来越香
}
Subtask 3
有了 Subtask 2 的经验,不难想到一定存在一个数
显然
把
但现在最多还是要询问
经过尝试,我下面这种二分方法也可以 AC。(当然,手工二分最保险)
code:
bool Comp(int i,int j){ //比较i的价值和j的价值
int l=1,r=14;
static int a[N],b[N];
while(l<r){
int mid=(l+r)>>1;
memset(a,0,sizeof(a));
a[i]=a[j]=mid;
playRound(a,b);
if(b[i]>mid&&b[j]<=mid)return false;
if(b[i]<=mid&&b[j]>mid)return true;
if(b[i]<mid&&b[j]<mid){
r=mid-1;
}
else{
l=mid+1;
}
}
return false;
}
int greaterValue(int n, int W) {
return Comp(0,1);
}
Subtask 4
最多询问
这个
设比较的物品分别为
有了比较方法下面只需要找个东西排个序啦!
stable_sort 可以胜任。(用法与 sort 一摸一样)
注:做交互题比较多的同学一定明白 sort 这个玩意千万不要用。由于它过于追求速度所以当递归到较小的区间后就会改成插入排序之类的,这样就浪费了很多询问。
code:
bool cmp(int i,int j){
int tmp[N]={},gugu[N];
tmp[i]=tmp[j]=100;
playRound(tmp,gugu);
return gugu[i]<=100;
}
void allValues(int n, int W, int *p) {
if(W==(n<<1)){
static int a[N];
memset(a,0,sizeof(a));
for(int i=0;i<n;++i){
a[i]=i;
}
stable_sort(a,a+n,cmp); //好东西,其实就是归并排序
for(int i=0;i<n;++i){
p[a[i]]=i+1;
}
}
}
Subtask 5
重头戏。(否则怎么可能占一半一上的分数呢?)
首先有个很显然的做法就是结合 Subtask 3 和 Subtask 4,用 stable_sort 排个序,这样大约询问
然鹅只能询问
类似于 Subtask 2 的做法,维护权值在
每次都可以找到一个权值
边界即
这样只需要询问
现在的问题就在于如何找到一个
可以直接暴力枚举,然后进行判断。由于我们只想去知道能否把集合分成两半,所以并不需要通过询问交互库来判断。构造一个价值依次为
你问我怎么求?像我这种懒人就直接复制交互库的代码然后瞎魔改一下了。。(没说过不让复制交互库吖^_^) 其实就是一个背包。
还有一些细节写到注释里了。
code:
//别看挺长的,check函数都是复制交互库的qwq。
bool check(int x,int l,int r){ //判断是否能把 [l,r] 切成两部分
int cache[2][205];
int num[2][205];
char taken[N][205];
memset(cache[1],0,sizeof(cache[1]));
memset(num[1],0,sizeof(num[1]));
const int W=100;
for(int i=0;i<N;++i) {
int v=(i>=l&&i<=r)?x+1:1;
int ii=i&1;
int o=ii^1;
for(int j=0;j<=W;++j) {
cache[ii][j]=cache[o][j];
num[ii][j]=num[o][j];
taken[i][j]=0;
}
for(int j=W;j>=v;--j) {
int h=cache[o][j-v]+i+1;
int hn=num[o][j-v]+1;
if(h>cache[ii][j]||(h==cache[ii][j]&&hn>num[ii][j])){
cache[ii][j]=h;
num[ii][j]=hn;
taken[i][j]=1;
}
else{
taken[i][j]=0;
}
}
}
int cur=W;
static int qwq[N];
for(int i=N-1;i>=0;--i) {
int tmp=taken[i][cur]?((i>=l&&i<=r)?x+1:1):0;
qwq[i]=tmp;
cur-=tmp;
}
int jb=qwq[l];
for(int i=l+1;i<=r;++i)if(qwq[i]^jb)return true; //对于集合中的位置,存在两个位置放个红色石子数量不同就说明可以把集合分开。
return false;
}
int get_val(int l,int r){
for(int i=1;i<=100/(r-l+1);++i){ //暴力枚举是否合法
if(check(i,l,r))return i;
}
exit(-1);
}
void Solve(int l,int r,vector<int> &vec,int *ans){
if(l==r){
ans[vec[0]]=l+1;
return;
}
static int a[N],b[N];
memset(a,0,sizeof(a));
int w=get_val(l,r);
for(int i=0;i<(int)vec.size();++i){
a[vec[i]]=w;
}
playRound(a,b);
vector<int> L,R;
for(int i=0;i<(int)vec.size();++i){
if(b[vec[i]]<=w)L.push_back(vec[i]);
else R.push_back(vec[i]);
}
Solve(l,l+L.size()-1,L,ans);
Solve(l+L.size(),r,R,ans);
}
void allValues(int n, int W, int *p) {
if(W==n){
vector<int> myh; //实在不知道用什么变量名了就用我npy的名字。
for(int i=0;i<n;++i){
myh.push_back(i);
}
Solve(0,n-1,myh,p); //开心分治
}
}
最后提醒一句:多测不清空,爆蛋两行泪。
最终全部代码:
#include "koala.h"
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
using namespace std;
void playRound(int*,int*);
#define N 100
int minValue(int n, int W) {
static int a[N],b[N];
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
a[0]=1;
playRound(a,b);
for(int i=0;i<n;++i){
if(b[i]<=a[i])return i;
}
return 114514;
}
int maxValue(int n, int W) {
static int a[N],b[N];
memset(a,0,sizeof(a));
for(int i=0;i<n;++i){
a[i]=1;
}
playRound(a,b);
memset(a,0,sizeof(a));
for(int i=0;i<n;++i){
if(b[i]>1)a[i]=2;
}
playRound(a,b);
memset(a,0,sizeof(a));
for(int i=0;i<n;++i){
if(b[i]>2)a[i]=4;
}
playRound(a,b);
memset(a,0,sizeof(a));
for(int i=0;i<n;++i){
if(b[i]>4)a[i]=11;
}
playRound(a,b);
for(int i=0;i<n;++i){
if(b[i]>11)return i;
}
return 1919810;
}
bool Comp(int i,int j){
int l=1,r=14;
static int a[N],b[N];
while(l<r){
int mid=(l+r)>>1;
memset(a,0,sizeof(a));
a[i]=a[j]=mid;
playRound(a,b);
if(b[i]>mid&&b[j]<=mid)return false;
if(b[i]<=mid&&b[j]>mid)return true;
if(b[i]<mid&&b[j]<mid){
r=mid-1;
}
else{
l=mid+1;
}
}
return false;
}
int greaterValue(int n, int W) {
return Comp(0,1);
}
bool cmp(int i,int j){
int tmp[N]={},gugu[N];
tmp[i]=tmp[j]=100;
playRound(tmp,gugu);
return gugu[i]<=100;
}
bool check(int x,int l,int r){
int cache[2][205];
int num[2][205];
char taken[N][205];
memset(cache[1],0,sizeof(cache[1]));
memset(num[1],0,sizeof(num[1]));
const int W=100;
for(int i=0;i<N;++i) {
int v=(i>=l&&i<=r)?x+1:1;
int ii=i&1;
int o=ii^1;
for(int j=0;j<=W;++j) {
cache[ii][j]=cache[o][j];
num[ii][j]=num[o][j];
taken[i][j]=0;
}
for(int j=W;j>=v;--j) {
int h=cache[o][j-v]+i+1;
int hn=num[o][j-v]+1;
if(h>cache[ii][j]||(h==cache[ii][j]&&hn>num[ii][j])){
cache[ii][j]=h;
num[ii][j]=hn;
taken[i][j]=1;
}
else{
taken[i][j]=0;
}
}
}
int cur=W;
static int qwq[N];
for(int i=N-1;i>=0;--i) {
int tmp=taken[i][cur]?((i>=l&&i<=r)?x+1:1):0;
qwq[i]=tmp;
cur-=tmp;
}
int jb=qwq[l];
for(int i=l+1;i<=r;++i)if(qwq[i]^jb)return true;
return false;
}
int get_val(int l,int r){
for(int i=1;i<=100/(r-l+1);++i){
if(check(i,l,r))return i;
}
exit(-1);
}
void Solve(int l,int r,vector<int> &vec,int *ans){
if(l==r){
ans[vec[0]]=l+1;
return;
}
static int a[N],b[N];
memset(a,0,sizeof(a));
int w=get_val(l,r);
for(int i=0;i<(int)vec.size();++i){
a[vec[i]]=w;
}
playRound(a,b);
vector<int> L,R;
for(int i=0;i<(int)vec.size();++i){
if(b[vec[i]]<=w)L.push_back(vec[i]);
else R.push_back(vec[i]);
}
Solve(l,l+L.size()-1,L,ans);
Solve(l+L.size(),r,R,ans);
}
void allValues(int n, int W, int *p) {
if(W==(n<<1)){
static int a[N];
memset(a,0,sizeof(a));
for(int i=0;i<n;++i){
a[i]=i;
}
stable_sort(a,a+n,cmp);
for(int i=0;i<n;++i){
p[a[i]]=i+1;
}
}
else{
vector<int> myh;
for(int i=0;i<n;++i){
myh.push_back(i);
}
Solve(0,n-1,myh,p);
}
}
Froggy's blog