Codeforces Round #699 (Div. 2)A-E题解

A-Space Navigation

题目大意:在坐标系中你的初始位置为(0,0)UDRL分别对应向上,向下,向右,向左操作,告诉你目标点位和当前已执行的操作。问能否通过删除部分操作到达目标点。

思路:确定目标点和当前位置,通过这两个坐标来确定要删除的操作及其个数,如果原操作中有对应数量的操作,则可以完成目标,否则不行。

#include <iostream>
#include <cstring>
using namespace std;
string p;
int ox,oy,nx,ny,t,cu,cd,cr,cl;
int main()
{
	cin>>t;
	while(t--)
	{
		cin>>ox>>oy; //目标点位 
		cu=cr=cd=cl=nx=ny=0;
		cin>>p;
		for(int a=0;a<p.length();a++) //找到当前点位 
		{
			if(p[a]=='U')
			{
				ny++;
				cu++;
			}else if(p[a]=='D')
			{
				ny--;
				cd++;
			}else if(p[a]=='R')
			{
				nx++;
				cr++;
			}else 
			{
				nx--;
				cl++;
			}
		}
		bool can=true;
		nx-=ox;
		ny-=oy;
		if(nx>=0)   //统计横坐标偏差 ,并判断是否有对应的数目 
		{
			if(cr<nx)can=false;
		}else
		{
			if(cl<-nx)can=false;
		}
		if(ny>=0)   //统计纵坐标偏差 ,并判断是否有对应的数目 
		{
			if(cu<ny)can = false;
		}else
		{
			if(cd<-ny)can = false;
		}
		cout<<(can?"YES":"NO")<<endl;
	}
	
}

B-New Colony

题目大意:给一个数组代表山的高度,定义一种操作投石头:如果h[i]>=h[i+1]石头就会接着滚下去。否则石头停下来当前的山的高度加一。超出数组的长度后就会加入收集系统,,问第k个石头的位置。
思路:开个数组直接模拟。

#include <vector>
#include <iostream>
#include <cstring>
using namespace std;
int arr[105],n,k,t;
vector<int> ve;
int main()
{
	
	cin>>t;
	while(t--)
	{
		cin>>n>>k;
		for(int a=1;a<=n;a++)cin>>arr[a];
		for(int a=0;a<k;a++)//模拟过程 
		{
			int b=1;
			for(b=1;b<=n;b++)
			{
				if(b==n)
				{
					b=-1;
					break;
				}else
				{
					if(arr[b]<arr[b+1])
					{
						arr[b]++;
						break;
					}
				}
			}
			if(a==k-1)
			{
				cout<<b<<endl;
				break;
			}
			if(b==-1)
			{
				cout<<b<<endl;
				break;
			}
		}
	}
}

C-Fence Painting

题目大意:你有n块篱笆,第i块的颜色是ai,你想要的重新粉刷,粉刷后第i块是bi。你邀请m个画家来画,第i个画家按顺序到,同时一个画家可以把一个篱笆染成一个对应的颜色(不能拒绝,每个画家必须画一个篱笆),第i个画家的颜色是ci。问:你能否把篱笆染成目标效果。
思路:颜色可以被染色,对于不被需要的颜色:假设第i个画家的颜色被第j块篱笆需要,那么把在他之前的不被需要的颜色都涂到j去等着被他染掉就好了。所以从后往前遍历画家,如果当画家被需要就安排到对应的位置去,如果不被需要,就填图到i+1画家的位置就好了。注意处理不需要更改颜色的情况(我通过预先找到与最后一个画家颜色相同的篱笆 来处理)。

#include <iostream>
#include <map>
#include <vector>
using namespace std;
const int maxx = 100005;
vector<int> cind[maxx];
map<int,int> change;
int t,m,n,ai[maxx],bi[maxx],ci[maxx],ind[maxx],allneed=0;
int main()
{
	cin>>t;
	while(t--)
	{
		change.clear();
		allneed=0;
		for(int a=0;a<maxx;a++)
		{
			cind[a].clear();
			ind[a]=0;
		}
		
		cin>>n>>m;
		for(int a=1;a<=n;a++)
		{
			cin>>ai[a];
		}
		for(int a=1;a<=n;a++)
		{
			cin>>bi[a];
			if(bi[a]!=ai[a])
			{
				change[bi[a]]++;   //表示要改为bi[a]的颜色的篱笆的数量 
				allneed++;         //总共要改颜色的篱笆的数量 
				cind[bi[a]].push_back(a);  //把要改为bi[a]的颜色的篱笆的位置存起来 
			}
		}
		for(int a=1;a<=m;a++)
		{
			cin>>ci[a];
		}
		
		for(int a=1;a<=n;a++)   //找到可以涂最后一个篱笆的画家 
		{
			if(bi[a] == ci[m])
			{
				ind[m+1] = a;
				break;
			}
		}
		for(int a=m;a>0;a--)
		{
			if(change[ci[a]]>0)          //如果这一个画家的颜色是被需要的那么就把他涂到对应位置去。 
			{
				ind[a] = cind[ci[a]].back();   //填入位置 
				cind[ci[a]].pop_back();        //删除已被填了的位置 
				change[ci[a]]--;               //减少标记 和 待填图数 
				allneed--;
			}else    //如果不得需要就  把他填图到 会被后面画家染色的位置上 
			{
				ind[a] = ind[a+1];
			}
		}
		if(ind[m] && allneed==0)    //如果最后一个篱笆被涂上了,并且没有待填图的篱笆就成功了 
		{
			cout<<"YES"<<endl;
			cout<<ind[1];
			for(int a=2;a<=m;a++)
			{
				cout<<" "<<ind[a];
			}
			cout<<endl;
		}else  //否则失败 
		{
			cout<<"NO"<<endl;
		}
		
	}
}

D-AB Graph

题目大意:给你一个有向图由n个点组成,任意两点之间都有来回路,且被标记为a或b,找出路径序列为回文串(左右对称)其长度为m的访问方式。
思路:只有路径标记只有a和b是关键。如果m为奇数直接找两个点来回走就好了,如果m为偶数:首先是一个特殊情况如果n==2那么1->2和2->1标记一定要相同不不然无解;在n>2时:找到三个点满足(ma[a][b] == ma[c][a] && ma[b][a]== ma[a][c])(c和b可以是一个点),如果m/2为奇数那么序列是:b->a->b…->a->c->a…->c;如果m/2为偶数那么序列是:a->b->a…->a->c->a…->a

#include <iostream>
#include <cstring>
using namespace std;
const int maxx = 1005;
int t,n,m,ma[maxx][maxx];
string tmp;
int main()
{
	cin>>t;
	while(t--)
	{
		cin>>n>>m;
		for(int a=1;a<=n;a++)
		{
			cin>>tmp;
			for(int b=1;b<=n;b++)
				ma[a][b]=tmp[b-1];
		}
		if(n==2 && m%2==0)  //处理特殊情况 
		{
			if(ma[1][2] == ma[2][1])
			{
				cout<<"YES"<<endl;
				cout<<1;
				for(int a=0;a<m;a++)
				{
					cout<<" "<<(a%2==0?2:1);
				}
				cout<<endl;
			}else
			{
				cout<<"NO"<<endl;
			}
			continue;
		}
		if(m%2==1) //m为奇数时 
		{
			cout<<"YES"<<endl;
			cout<<1;
			for(int a=0;a<m;a++)
			{
				cout<<" "<<(a%2==0?2:1);
			}
			cout<<endl;
		}else //偶数时 
		{
			int p1=0,p2=0,p3=0;
			for(int a=1;a<=n;a++)   //开三层for暴力找点  a!=b,c  但是b可以等于c 
			{
				for(int b=1;b<=n;b++)
				{
					if(a==b)continue;
					for(int c=1;c<=n;c++)
					{
						if(a==c )continue;
						if(ma[a][b] == ma[c][a] && ma[b][a]== ma[a][c])
						{
							p1 = a;
							p2 = b;
							p3 = c;
							break;
						}
					}
					if(p1)break;
				}
				if(p1)break;
			}
			if(p1)   //输出序列 
			{
				cout<<"YES"<<endl;
				if(m/2%2==0)
				{
					cout<<p1;
					for(int a=0;a<m/2;a++)
					{
						cout<<" "<<(a%2==0?p2:p1);
					}
					for(int a=0;a<m/2;a++)
					{
						cout<<" "<<(a%2==0?p3:p1);
					}
					cout<<endl;
				}else
				{
					cout<<p2;
					for(int a=0;a<m/2;a++)
					{
						cout<<" "<<(a%2==0?p1:p2);
					}
					for(int a=0;a<m/2;a++)
					{
						cout<<" "<<(a%2==0?p3:p1);
					}
					cout<<endl;
				}
			}else
			{
				cout<<"NO"<<endl;
			}
		}
		
	}
}

E-Sorting Books

题目大意:给一个数组每次可以把其中一个数移到最后,问最少移动几次可以是所有的相同的数字相邻。
思路:把问题转化为从怎么移动可以让最多的数字不动。规定几个数字R[i]从表示数字i的最靠右的位置,L[i]表示i最靠左的位置,cnt[i]表示从当前位置向后数字i的个数,book[i]用来存储原数组。从后向前看,dp[i]代表从i开始向后想序列中最后可以让几个数字不动来实现效果。对于第i个数字,分两种情况:1、这个这个数字不是这一种数字中最靠左的,那么就让当前出现次数最多的数字保持不动dp[i]=max( dp[i+1],cnt[ book[i] ] )。2、这个数字是这一种数字中最靠左的,此时如果让这一种数字都不动那么可以不动的数字的数量就是cnt[ book[i] ]+dp[ R[ book[i] ] ] , 如果让这一种数字移动可以保持不动的最大数量就是dp[i+1]。

#include <iostream>
#include <cstring>
using namespace std;
const int maxx = 500005;
int L[maxx],R[maxx],dp[maxx],cnt[maxx],book[maxx],n;
int main()
{
	cin>>n;
	for(int a=1;a<=n;a++)
	{
		cin>>book[a];
		if(!L[book[a]])
		{
			L[book[a]] = a;
		}
		R[book[a]] = a;
	}
	memset(dp,0,sizeof(dp));
	for(int a = n;a>0;a--)
	{
		cnt[book[a]]++;
		
		if(a == L[book[a]])
		{
			int re = cnt[book[a]]+dp[R[book[a]]+1];
			int mo = dp[a+1];
			dp[a] = max(re,mo);
		}else
		{
			int re = cnt[book[a]];
			int mo = dp[a+1];
			dp[a] = max(re,mo);
		}
	}
	cout<<n-dp[1]<<endl;
}

不懂的可以看这篇博客,我也是看他的理解的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值