Educational Codeforces Round 169题解A~D

A题

题意

给定一个正整数集合,判断能否在该集合中添加一个改前不存在的元素,使得加入的元素成为所有原集合元素的最近值(绝对值最小)。

思路

通过模拟,我们可以发现:符合题目条件的集合有以下几个特征:

元素数量为1,恒满足

元素数量为2,需要两个元素之间的abs(差值)大于1,满足,否则不满足

元素数量>2,恒不满足

代码

#include <iostream>
#include <cstring>
#include <queue>
#include <map>
#include <set>
#include <algorithm>
#include <cmath>
#include <vector>
#include <stack>
using namespace std;
typedef long long LL;
typedef pair<LL,LL> PII;//开long long,求求你了,记得开long long 
const LL N=1e5+10;
const LL INF=1e18;
const double small=1e-16;
//千万不要用puts()和gets(),求求你了 


void solve()
{
	int n;
	cin>>n;
	set<LL> has;
	for(int i=1;i<=n;i++)
	{
		LL x;
		cin>>x;
		has.insert(x);
	}
	if(has.size()<=1)cout<<"YES"<<endl;
	else if(has.size()==2)
	{
		LL num[5];
		int i=0;
		for(auto x:has)
		{
			num[i++]=x;
		}
		if(abs(num[0]-num[1])<=1)cout<<"NO"<<endl;
		else cout<<"YES"<<endl;
	}
	else cout<<"NO"<<endl;
}

int main()
{
    
	int t=1;
	cin>>t;
	while(t--)
	{
		solve();
	}
	return 0;
}

B题

题意

有一条正方形方格连成的链,每个方格之间有一扇门可以选择锁或不锁,然后分别给出a和b可以出现的方格区间,求最少要锁住多少扇门能够使得无论a和b在各自区间内如何出现,都不能通过未锁的门相互到达(注:a和b初始不会出现在同一个方格内)

思路

得到的区间关系主要如下:

区间不相交:中间堵一扇门就行

区间相交:先把相交区间所涉及的每一扇门关掉,然后考虑边界问题:左右端点重复时,该端点不需要堵外侧的一扇门

代码

#include <iostream>
#include <cstring>
#include <queue>
#include <map>
#include <set>
#include <algorithm>
#include <cmath>
#include <vector>
#include <stack>
using namespace std;
typedef long long LL;
typedef pair<LL,LL> PII;//开long long,求求你了,记得开long long 
const LL N=1e5+10;
const LL INF=1e18;
const double small=1e-16;
//千万不要用puts()和gets(),求求你了 


void solve()
{
	LL l,r;
	LL L,R;
	cin>>l>>r;
	cin>>L>>R;
	LL ll=min(l,L);//得到覆盖的方格区间
	LL rr=max(R,r);
	if(r<L||R<l)//如果区间没有相交,就只需要中间堵1扇门就行
	{
		cout<<"1"<<endl;
	}
	else
	{
		LL xl=max(l,L);//得到相交的区间
		LL xr=min(R,r);
		LL need=xr-xl+1+1;//这里加2是因为假设边界需要(比如a的最右边的格子在b的区间中间)
		if(l==L)need--;//边界的不需要再堵门,因为a和b不会同时出现在一个格子里
		if(R==r)need--;
		cout<<need<<endl;
	}
	
}

int main()
{
    
	int t=1;
	cin>>t;
	while(t--)
	{
		solve();
	}
	return 0;
}

C题

题意

有一堆给定价值的物品,A先手,A和B轮流取一份物品加到自己的价值里面,直到物品取完;A的目标是使得价值A-价值B的值最大,B的目标是使得价值A-价值B的值最小(换句话说:每个人都想要自己取得的物品的总价值最大);现在B可以提前进行以下操作:B有k个价值,能够将这些价值加到某些物品中,使得该物品的价值增加,从而实现自己的目标;现在求B经过最佳操作后能获得的A-B的值的最小值。

思路

读题时要注意:是总共能加k个价值,而不是每个物品最多加k价值(赛时太唐了,wa2了好几发)

既然是要最大化自己的价值,那肯定是贪心选价值最大的物品,那么我们可以将物品按照价值降序排序,偶数下标是A的物品,奇数下标是B的物品,B的价值肯定不能加到A的物品上,所以只能加到奇数位上,但是不能加太多,加太多就会破坏原有的顺序导致B的价值加到移位后的偶数位上,这个时候需要更多的额外价值抵消A增加的价值;所以加到与上一位相等就可以停止了。

代码

#include <iostream>
#include <cstring>
#include <queue>
#include <map>
#include <set>
#include <algorithm>
#include <cmath>
#include <vector>
#include <stack>
using namespace std;
typedef long long LL;
typedef pair<LL,LL> PII;//开long long,求求你了,记得开long long 
const LL N=2e5+10;
const LL INF=1e18;
const double small=1e-16;
LL num[N];
//千万不要用puts()和gets(),求求你了 
bool cmd(LL a,LL b)
{
	return a>b;
}

void solve()
{
	LL n,k;
	cin>>n>>k;
	for(int i=1;i<=n;i++)
	{
		cin>>num[i];
	}
	sort(num+1,num+1+n,cmd);
	LL A=0,B=0;
	LL res=0;
	for(int i=1;i<=n;i++)
	{
		//cout<<num[i]<<" ";
		if(i%2==1)
		{
			res+=num[i];
		}
		else 
		{
			LL need=num[i-1]-num[i];
			need=min(k,need);
			k-=need;
			num[i]+=need;
			res-=num[i];
		}	
	}
	//cout<<endl;
	//cout<<A<<" "<<B<<endl;
	cout<<res<<endl;
}

int main()
{
    
	int t=1;
	cin>>t;
	while(t--)
	{
		solve();
	}
	return 0;
}

D题

题意

有编号从1~n的n个城市,每个城市有2种不同颜色传送门(一共只有4种传送门),然后有至少有一个相同颜色传送门的城市i,j之间可以相互通行,消耗为|i-j|,给定q个询问,询问从城市a到城市b最少的消耗是多少?

思路

可以证明:如果两个城市a和b可以直接相互通行,消耗肯定比其他方案要少;如果不能相互通行,则引进一个传送门组合和起点终点组合不同的城市z作为中转站,就可以通行:

a<z<b:res=b-a+1;

z<a<b:res=(b+a)-2*z

a<b<z:res=2*z-(b+a)

取最小值,

这里直接找编号离b最近的城市

代码

#include <iostream>
#include <cstring>
#include <queue>
#include <map>
#include <set>
#include <algorithm>
#include <cmath>
#include <vector>
#include <stack>
using namespace std;
typedef long long LL;
typedef pair<LL,LL> PII;//开long long,求求你了,记得开long long 
const LL N=1e5+10;
const LL INF=1e18;
const double small=1e-16;
//千万不要用puts()和gets(),求求你了 
map<string,int> op;
set<int> has[10];
map<int,string> type;
int n,q;
LL found_near(vector<int> num,int a,int b)
{
	//mid<=l
	int ans=1e9;
	if(a>b)swap(a,b);
	int l=0,r=num.size()-1;
	while(l<r)
	{
		int mid=(l+r)/2;
		if(num[mid]>=b)r=mid;
		else l=mid+1;
	}
	//ans=min(ans,abs(num[l]-a)+abs(num[l]-b));
	//mid>=r
	if(num[l]>=b) ans=min(ans,2*num[l]-a-b);
	if(num[l]<b&&l!=0)ans=min(ans,abs(num[l-1]-a)+abs(num[l-1]-b));
	 
	return ans;
}

void solve()
{
	cin>>n>>q;
	for(int i=1;i<=6;i++)has[i].clear();
	type.clear();
	
	for(int i=1;i<=n;i++)
	{
		string s;
		cin>>s;
		has[op[s]].insert(i);//每种城市的编号
		type[i]=s;//城市i的传送门类型
	}
	//for(int i=1;i<=6;i++)sort(has[i].begin(),has[i].end());
	while(q--)
	{
		int l,r;
		scanf("%d%d",&l,&r);
		string op1=type[l];
		string op2=type[r];
		int type1=op[op1];
		int type2=op[op2];
		bool flag=false;
		for(int i=0;i<2;i++)
		{
			for(int j=0;j<2;j++)
			{
				if(op1[i]==op2[j])flag=true;//如果能够直接通行,优先直接通行
			}
		}
		if(flag)
		{
			cout<<abs(l-r)<<endl;
			continue;
		}
		else
		{
			int res=1e9;
			if(l>r)swap(l,r);//否则,找到l,r之间的一个不同类型城市或者离r或l最近的城市

			for(int i=1;i<=6;i++)
			{
				if(i==type1||i==type2)continue;//需要和起点终点城市类型不同
				if(has[i].size()==0)continue;
				auto ans=has[i].lower_bound(r);//手写二分有些问题,TLE了
				if(ans!=has[i].end()) {res=min(res,(*ans)*2-l-r);}//找到离y编号最近的城市
				if(ans!=has[i].begin()) {ans--;res=min(res,abs(*ans-l)+abs(*ans-r));}
			}
			if(res!=1e9)cout<<res<<endl;
			else cout<<"-1"<<endl;
		}
	}
}

int main()
{
    
	int t=1;
	cin>>t;
	op["BG"]=1;
	op["BR"]=2;
	op["BY"]=3;
	op["GR"]=4;
	op["GY"]=5;
	op["RY"]=6;
	while(t--)
	{
		solve();
	}
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值