A.Traveling Salesman Problem
最短路径即矩形缺一角,我们将那一角做平移,即可得一个矩形,矩形周长即最短路径
#include <bits/stdc++.h>
#define eps 1e-8 //多2
#define inf 0x3f3f3f3f
#define PI acos(-1) //π
#define for(l,r) for(int i = l; i <= r; i++)
#define NO {puts("NO") ; return ;}
#define YES {puts("YES") ; return ;}
using namespace std;
typedef long long ll;
int TEST; //测试案例数
const int N = 1e5+5; //数组长度
int n,m;
int x[200],y[200];
void solve()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n;
int xl=0,xr=0,yl=0,yr=0;
for(0,n-1){
cin >> x[i] >> y[i];
xl = min(x[i],xl);
xr=max(x[i],xr);
yl=min(yl,y[i]);
yr=max(y[i],yr);
}
cout << 2*(yr+xr-yl-xl) << endl;
}
int main(void)
{
ios::sync_with_stdio(false);
cin.tie(0);
cin >> TEST;
while(TEST--)
solve();
return 0;
}
B.Optimal Reduction
只要中间没有凹点即可,反过来说,只要排列单调增、单调减或者先增后减都是满足的
#include <bits/stdc++.h>
#define eps 1e-8 //多2
#define inf 0x3f3f3f3f
#define PI acos(-1) //π
#define for(l,r) for(ll i = l; i <= r; i++)
#define NO {puts("NO") ; return ;}
#define YES {puts("YES") ; return ;}
using namespace std;
typedef long long ll;
int TEST; //测试案例数
const int N = 1e5+5; //数组长度
int n;
int q[N];
void solve()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n;
int minx=0;
for(0,n-1)
cin >> q[i];
int ind=0;
if(n == 1) //长度为1,直接输出YES
YES;
if(n >= 2 && q[0] > q[1]) //长度大于1且开始是递减,则之后只能递减,若有递增,则输出NO
{
for(0,n-2){
if(q[i] >= q[i+1])
ind = i+1;
else
break;
}
if(ind == n-1)
YES;
NO;
}
for(0,n-2){ //长度大于1且开始是递增,之后可以一直递增,或者某一时刻出现递减后并持续递减
if(q[i] <= q[i+1])
ind = i+1; //找到递增的结束点(可能是递减的开始点)
else
break;
}
if(ind == n-1) //递增序列
YES;
for(ind,n-2) //若运行至此,则为非递增序列,判断是否满足上述条件,即出现递减后一直递减
{
if(q[i] < q[i+1]) //若再次出现递增,则输出NO
NO;
}
YES; //满足先增后减条件
}
int main(void)
{
ios::sync_with_stdio(false);
cin.tie(0);
cin >> TEST;
while(TEST--)
solve();
return 0;
}
C.Build Permutation
首先,根据以下本人描述的做法,可以证明n个元素一定可以组成某个排列满足本题描述条件。
f(n)表示求出0~n-1这n个元素满足题意的排列,并存入ans[1]到ans[n]中。
易知:
f(1): 0
f(2): 1 0
f(3): 1 0 2
f(4): 0 3 2 1
f(5): 4 3 2 1 0
…
根据递归来做,假设f(n)可求出n个元素满足条件的排列。即求0~n-1满足条件的排列,对于n-1,我们对其开方得到整数x,判断
x
2
x^{2}
x2与n-1的大小,若相等,则说明n-1为完全平方数,我们之间将答案赋值为n-1,n-2,… ,0,则据题意依次加上0,1,…,n-1后,每一项都是n-1(即
x
2
x^{2}
x2)这个完全平方数。
若n-1大于
x
2
x^{2}
x2(不会出现小于的情况),则将x加1,此时
x
2
x^{2}
x2一定大于n-1,则我们向下调用 f(
x
2
x^{2}
x2-(n-1)),并将答案数组的第(
x
2
x^{2}
x2-n+2)项至最第n项依次赋值为
x
2
x^{2}
x2-i+1(i为项数),则这些项每一项都是
x
2
x^{2}
x2这个完全平方数。
依次如此递归处理下去。
最后给出退出条件,即n=1与n=2时,给答案数组赋值。为了加快速度,可以给出更多的退出条件,如n=3,4…
#include <bits/stdc++.h>
#define eps 1e-8 //多2
#define inf 0x3f3f3f3f
#define PI acos(-1) //π
#define for(l,r) for(ll i = l; i <= r; i++)
using namespace std;
typedef long long ll;
int TEST; //测试案例数
const int N = 1e5+5; //数组长度
int n;
int ans[N];
void f(int n)
{
if(n == 1){
ans[1]=0;
return;
}
if(n == 2)
{
ans[1]=1;
ans[2]=0;
return;
}
int x = int(sqrt(n-1));
if(x*x == n-1){
for(1,n)ans[i]=n-i;
return;
}if(x*x < n-1) x++;
int next = x*x-n+1;
for(next+1,n) ans[i] = x*x-i+1;
f(next);
}
void solve()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n;
f(n);
for(1,n)cout << ans[i] << " ";
cout << endl;
}
int main(void)
{
ios::sync_with_stdio(false);
cin.tie(0);
cin >> TEST;
while(TEST--)
solve();
return 0;
}
D.Tournament Countdown
分析:
n=1时,询问一次直接得出答案
n>=2时,以四个人为一组,从最底层往上,每按序从左向右取四个人,1和2比,3和4比,分别有一胜一负,假设输的为2、3,则2和3赢的次数一样多,而1、4进行比赛,假设赢的是1,则1至少比4多赢一场(设2、3分别赢i场,则4赢了i+1场,由于如果比赛到此不结束,那么1可以继续参加后续比赛,故1至少赢i+2场)。所以每从左往右取四个人(不论是最底层还是非最底层),一定有两个人赢的场数相等(第一轮淘汰的人),剩下两人,一人比第一轮淘汰的人多赢一场,另一人比第一轮淘汰的人至少多赢两场。
例如n=3的一种情况:
策略:
每次选四个人,编号为1、2、3、4 。做两次询问得出这四个人中赢的最多的人:
(假设四人到目前赢的场数均为i,则1和2中必定有一人赢的场数停留在i,3、4同理)
询问1,3:
若1赢的多,则说明2在第一轮和1比赛时被淘汰,若3赢的多,则说明4在第一轮和3比赛时被淘汰。
不妨假设1赢的多,那我们再询问1,4:
若1赢的多,则说明4人中1赢的最多,即只有1能够进入之后的比赛,此时我们将1对应的选手存入数组。同理,若4赢的多,则赢的场数:4>1>3=2,4进入之后比赛,将4对应选手存储。
依次处理 2 n 2^{n} 2n个选手,我们又得到了一个大小为 2 n − 2 2^{n-2} 2n−2的数组,更新n并重复上述操作,直到n<=1时退出,此时仅余两位选手(特殊情况:当初始n=1时,一次处理后,n=0,仅余一位选手,直接对其输出),询问一次得出结果。
可行性分析
实现:
#include <bits/stdc++.h>
#define eps 1e-8 //多2
#define inf 0x3f3f3f3f
#define PI acos(-1) //π
#define for(l,r) for(ll i = l; i <= r; i++)
#define NO {puts("NO") ; return ;}
#define YES {puts("YES") ; return ;}
using namespace std;
typedef long long ll;
int TEST; //测试案例数
const int N = 2e5+5; //数组长度
int n;
int winner;
vector<int> q;
vector<int> tmp;
int ww[N];
int ask(int a, int b)
{
cout << "? " << a << " " << b << endl;
int read;
cin >> read;
return read;
}
int my_power(int x)
{
return (1 << x);
}
void solve()
{
ios::sync_with_stdio(false);
cin.tie(0);
vector <int>().swap(q);
vector <int>().swap(tmp);
cin >> n;
int length = my_power(n);
if(n==1)
{
winner = ask(1,2);
cout << "! " << winner << endl;
return;
}
tmp.push_back(0);
for(1,length)
tmp.push_back(i); //初始化数组
int ind = length+1;
int ix = 1;
int t; //暂存返回值(0 1 2)
int length_this;
while(ind > 3)
{
q.swap(tmp);
tmp.clear();
tmp.push_back(0);
ind = 1;
ix = 1;
length_this = my_power(n);
while(ix <= length_this-3)
{
t = ask(q[ix],q[ix+3]);
if(t == 0)
{
t=ask(q[ix+1],q[ix+2]);
if(t == 1) tmp.push_back(q[ix+1]);
else tmp.push_back(q[ix+2]);
}
else if(t == 1)
{
t=ask(q[ix],q[ix+2]);
if(t==1) tmp.push_back(q[ix]);
else tmp.push_back(q[ix+2]);
}
else
{
t = ask(q[ix+1],q[ix+3]);
if(t==1)tmp.push_back(q[ix+1]);
else tmp.push_back(q[ix+3]);
}
ind+=1;
ix+=4;
}
n-=2;
}
if(n==0)
{
cout << "! " << tmp[1] << endl;
return;
}
// cout << tmp[1] << "---" << tmp[2] << endl;;
winner = ask(tmp[1],tmp[2])==1?tmp[1]:tmp[2];
cout << "! " << winner << endl;
// cout.flush();
return;
}
int main(void)
{
ios::sync_with_stdio(false);
cin.tie(0);
cin >> TEST;
while(TEST--)
solve();
return 0;
}