Educational Codeforces Round 129 (Rated for Div. 2)(A-E)题解

A. Game with Cards

题目链接:Problem - A - Codeforces

题意: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

题目链接:Problem - B - Codeforces

题意:有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

题目链接:Problem - C - Codeforces

题意:给两个长度为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

题目链接:Problem - D - Codeforces

题意:给一个数字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

题目链接:Problem - E - Codeforces

题意:有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;
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值