A. Game with Cards
题意:Alice和Bob打牌,两人分别有n和m张牌,每个人出的牌必须比前一张牌大,不然就输了,A输出两人分别先后手的情况。
思路:每人直接打出最大的牌,比较两人最大牌的大小,如果两人的最大牌相同,那么先手必赢。
代码实现:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e3+10;
const int inf=0x3f3f3f3f;
int a[maxn],b[maxn];
int main()
{
int t;
cin>>t;
while(t--)
{
int maxa=0;
int maxb=0;
int n,m;
cin>>n;
for(int i=1;i<=n;i++)
{
int x;
cin>>x;
maxa=max(maxa,x);//寻找Alice的最大牌
}
cin>>m;
for(int i=1;i<=m;i++)
{
int x;
cin>>x;
maxb=max(maxb,x);//寻找Bob的最大牌
}
if(maxa>=maxb)//Alice先手,最大牌相同Alice也能赢
cout<<"Alice\n";
else
cout<<"Bob\n";
if(maxa>maxb)//Alice后手
cout<<"Alice\n";
else
cout<<"Bob\n";
}
return 0;
}
B. Card Trick
题意:有n张牌的牌组,从上到下大小分别为a1,a2...an,进行m次洗牌,第i次洗牌将顶部bj张牌洗到底部。问第m次洗牌后最顶端的牌的大小
思路:如果直接模拟洗牌那么就太复杂了,我们只需要关注最顶端的牌,在未洗牌的状态,顶端为a1,将顶部x张牌放入底端,当前顶端为a[1+x],以此类推,最顶端的牌可以表示为a[(1+x)%n]。
代码实现:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e5+10;
const int inf=0x3f3f3f3f;
int a[maxn],b[maxn];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin>>t;
while(t--)
{
int n,m;
cin>>n;
int top=1;//记录最顶端的下标
for(int i=1;i<=n;i++)
cin>>a[i];
cin>>m;
for(int i=1;i<=m;i++)
{
int x;
cin>>x;
top+=x;
top%=n;
}
if(top==0)//因为n%n==0,所以没有这步会输入a[0](就是0)
top=n;
cout<<a[top]<<'\n';
}
return 0;
}
C. Double Sort
题意:给两个长度为n的数列,有如下操作,将两个数列的第i个元素同时与各自的第j个元素交换,问有没有可能将两个数列变成非递减数列,如果能,就输出其中一种做法,如果没有输出-1。
思路:假设如果两个数列没有重复元素,那么两个数列的各元素大小关系必须相同才可以排序,也就是说两数组第i大的数必须在同一位置。但是这题有重复数字,前面这种类似于离散化(大概?)的做法不能成立,相同的数字排序之后肯定连在一起,而另外一个数组在这段连续的区间必须满足非递减关系。把两个数组看成一个整体,先按照第一个数组排序,相同数字的地方根据第二个数组大小关系排序,排完后第一个数组肯定排好了,但第二数组只保证了在第一个数组相同数字的一块满足非递减,所以此时检查第二数组是否排好就可以验证。难点在于判断,排序的话可以随便。
代码实现:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e5+10;
const int inf=0x3f3f3f3f;
struct node{
int x,y;
}a[maxn],b[maxn],ans[maxn];
/*a存储两个数组,用于判断能否排序
b存储两个数组,用于寻找排序的方法
ans记录第i个元素和第j个元素互换*/
bool cmp1(const node a,const node b)
{
if(a.x!=b.x)//先按第一个数组排
return a.x<b.x;
else//第一个数组元素相同按第二个排
return a.y<b.y;
}
int main()
{
// ios::sync_with_stdio(false);
// cin.tie(0);
int t;
cin>>t;
while(t--)
{
int n;
cin>>n;
for(int i=1;i<=n;i++)//输入第一个数组
{
cin>>a[i].x;
b[i].x=a[i].x;
}
for(int i=1;i<=n;i++)//输入第二个数组
{
cin>>a[i].y;
b[i].y=a[i].y;
}
sort(a+1,a+1+n,cmp1);
bool f=1;
for(int i=1;i<=n-1;i++)
if(a[i].y>a[i+1].y)//如果第二个数组不是非递减
{
f=0;
break;
}
if(f)
{
int s=0;
for(int i=1;i<=n;i++)
{
int min=i;
for(int j=i+1;j<=n;j++)
{
if(b[min].x>=b[j].x&&b[min].y>=b[j].y)
min=j;//寻找第i小的数(第i次循环中最小的数)
}
if(min!=i)
{
ans[++s].x=min;
ans[s].y=i;
node temp=b[min];//交换
b[min]=b[i];
b[i]=temp;
}
}
cout<<s<<'\n';
for(int i=1;i<=s;i++)
{
cout<<ans[i].x<<' '<<ans[i].y<<'\n';
}
}
else
cout<<-1<<'\n';
}
return 0;
}
D. Required Length
题意:给一个数字n和x,n代表长度,x每次操作可以乘以自己十进制形式出现的数,(比如12可以乘1或2),问x最少几次操作可以变成长度为n的数,如果不可以输出-1。
思路:n的大小为[2,19],可以使用BFS,队列存储的是x分别乘以(除了0)自己各位上的数后的结果,因为要记录每一位上的数,所以用string表示就很方便,同时string还能记录长度,用一个map记录是否入队并且记录转换成该数字所需要的操作数,当队列首元素长度为n是,即查找成功。
代码实现:
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
using namespace std;
//unsigned long long可以表示10的19次方的数,样例n最大为19
int main()
{
int n;
cin >> n;
ull x;
cin >> x;
queue<ull> q;
map<ull, int> mp;//记录到达该数的操作数
mp[x]=0;
q.push(x);
int ans=-1;
while(!q.empty())
{
ull p=q.front();//取出队列首元素
q.pop();
string s="";
ull temp=p;
while(temp)//获取倒过来的p,实际上不影响,因为只需要知道长度和每位的数
{
s+=temp%10+'0';
temp/=10;
}
if(s.size()==n)//如果长度找到了
{
ans=mp[p];
break;
}
for(int i=0;i<s.size();i++)//遍历每一位数字
{
if(s[i]=='0')continue;
ull a = p*(s[i]-'0');
if(mp.find(a)==mp.end())//如果mp里没有这个数,就加入队列
{
mp[a]=mp[p]+1;
q.push(a);
}
}
}
cout<<ans;
return 0;
}
E. Labyrinth Adventures
题意:有n*n个方格的矩阵,从低到高行号为1-n,从左往右列号为1-n,层数的定义为从左下角开始,第n层为从(n,1)到(n,n),再从(n,n)到(1,n)。每层有两扇门,有个在每层横着的一行,还有一个在竖着的一列中,通往下一层,给m次询问,输入x1,y1,x2,y2,问两者最近距离
思路:首先先规定(x1,y1)和(x2,y2)之间的距离是从低层向高层,如果层数相同,直接返回其曼哈顿距离。低层的点首先要走到高层同一层去,然后再求曼哈顿距离,这途中会经过很多的门,所以要记录每层之间的门的最短距离(从该层第k1个门到目的地的第k2个门),数组需要用四个数据,但是最大有1e5层门,如果直接定义一个[1e5][1e5][][]内存会占用很大,此时可以用倍增算法,记录从i层的k1扇门到i+2^j层的k2扇门的距离,定义的四维数组的第二个元素代表“向上走”2^i层,因为1e5<2^17,因此第二个元素定义成18左右就行,非常省空间。根据二进制思想,2^i (0<=i<=17)之和可以表达1e5内任何数,计算过程中不断让低层的点往上走,直到走到目的地的上一层,因为通过该层的门走一步就到了目的地所在层,不需要再找门,直接求曼哈顿距离
代码实现:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e5+10;
inline ll dis(ll x1,ll y1,ll x2,ll y2)//计算曼哈顿距离
{
return abs(y2-y1)+abs(x2-x1);
}
ll d[maxn][25][2][2];//从第i层的第k1扇门向上走2^j层到i+2^j层的第k2扇门
ll x1[maxn],x2[maxn],y1[maxn],y2[maxn];//每层第0,1扇门的坐标
ll solve(ll ax,ll ay,ll bx,ll by)
{
ll rank1=max(ax,ay);//层数是坐标中大的那个
ll rank2=max(bx,by);
if(rank1>rank2)//方便处理,保证rank1比rank2小
{
swap(ax,bx);
swap(ay,by);
swap(rank1,rank2);
}
else if(rank1==rank2)
return dis(ax,ay,bx,by);
ll fly=rank2-rank1-1;//向上飞的层数,只需要到rank2-1层就行,因为通过rank2-1的门再走一步就是rank2层,在rank2层不需要到该层的门
ll to0=dis(ax,ay,x1[rank1],y1[rank1]);//到当前层第0扇门的距离
ll to1=dis(ax,ay,x2[rank1],y2[rank1]);//到当前层第1扇门的距离
for(int i=17;i>=0;i--)
{
if(fly-(1<<i)>=0)//二进制思想,总能将fly减到0
{
ll temp1=min(d[rank1][i][0][0]+to0,d[rank1][i][1][0]+to1);//从rank1层到rank1+2^i层第0扇门最小的距离
ll temp2=min(d[rank1][i][0][1]+to0,d[rank1][i][1][1]+to1);//从rank1层到rank1+2^i层第1扇门最小的距离
rank1+=(1<<i);//rank1向上走到rank1+2^i层
to0=temp1;//走到当前(变化后)第0扇门的步数
to1=temp2;//走到当前(变化后)第1扇门的步数
fly-=(1<<i);
}
}
//从rank1层到“rank2-1层通往rank2层的第0扇门”,该门出发走一步到rank2层,在走到目的地
//从rank1层到“rank2-1层通往rank2层的第1扇门”,该门出发走一步到rank2层,在走到目的地
//两者取小
ll ans=min(to0+1+dis(bx,by,x1[rank2-1]+1,y1[rank2-1]),to1+1+dis(bx,by,x2[rank2-1],y2[rank2-1]+1));
return ans;
}
int main() {
int n;
cin>>n;
for(int i=1;i<=n-1;i++)
{
cin>>x1[i]>>y1[i];//第0扇门,方向朝上
cin>>x2[i]>>y2[i];//第1扇门,方向朝右
}
for(int i=1;i<=n-2;i++)
{
//末尾加1是为了通过该门走上i+2^0层
//从第i层的第0扇门(方向朝上)到i+2^0层的第0扇门
d[i][0][0][0]=dis(x1[i]+1,y1[i],x1[i+1],y1[i+1])+1;
//从第i层的第1扇门(方向朝右)到i+2^0层的第0扇门
d[i][0][1][0]=dis(x2[i],y2[i]+1,x1[i+1],y1[i+1])+1;
//从第i层的第0扇门(方向朝上)到i+2^0层的第1扇门
d[i][0][0][1]=dis(x1[i]+1,y1[i],x2[i+1],y2[i+1])+1;
//从第i层的第1扇门(方向朝右)到i+2^0层的第1扇门
d[i][0][1][1]=dis(x2[i],y2[i]+1,x2[i+1],y2[i+1])+1;
}
for(int i=1;i<=17;i++)//2^17已经比1e5大了
for(int j=1;j+(1<<i)<n;j++)//从第j层到j+2^i层
{
for(int k1=0;k1<=1;k1++)//从第k1扇门出发
{
for(int k2=0;k2<=1;k2++)//到第k2扇门
{
//从第j层的第k1扇门到第j+2^i层的第k2扇门
//转变成先从j层的第k1扇门到j+2^(i-1)的第0扇或第1扇门再从该门出发再向上走2^(i-1)层并且到第k2层门,路径取小的那个
d[j][i][k1][k2]=min(d[j][i-1][k1][0]+d[j+(1<<(i-1))][i-1][0][k2],d[j][i-1][k1][1]+d[j+(1<<(i-1))][i-1][1][k2]);
}
}
}
int m;
cin>>m;
for(int i=1;i<=m;i++)
{
int ax,ay,bx,by;
cin>>ax>>ay>>bx>>by;
cout<<solve(ax,ay,bx,by)<<'\n';
}
return 0;
}