Codeforces Round #390 (Div.2) A~D

在这里插入图片描述
不知道为啥,这次这些题给的时间还是蛮多的。。。
比赛链接

T1_ Lesha and array splitting(水题)

在这里插入图片描述
在这里插入图片描述
——题意——
吐个槽,大多数时候,这种英文题看着是真的难受,不看样例解释加上揣测,根本不知道它要干啥。

本题是这样的,对一串数列分组,分为多个区间,要求每个区间内元素的和不为0。

——题解——
分为两种种情况讨论:
①所有元素都为0,那么怎么分也不可能符合题意,输出“NO”;
②存在不为0的元素,这样再分两种情况:
a.所有元素的和不为0,那么区间范围就是整个数列即可;
b.所有元素的和为0,那么从第一个元素遍历,直到前面这些元素加起来不为0,我们就把第一个区间划分到此处,剩下为第二个区间。

——Code——

#include<iostream>
using namespace std;
int a[101];
int n;
int sum[101];
int sum2[101];
int main()
{
	cin>>n;
	int cnt=0;
	bool flag=false;
	for(int i=1;i<=n;++i)
	{
		cin>>a[i];
		sum[i]=sum[i-1]+a[i];
		if(a[i]==0)
			++cnt;
	}
	if(cnt==n)
		cout<<"NO"<<endl;
	else if(sum[n]!=0)
	{
		cout<<"YES"<<endl;
		cout<<1<<endl;
		cout<<1<<" "<<n<<endl;
	}
	else
	{
		cout<<"YES"<<endl; 
		for(int i=1;i<=n;++i)
		{
			sum2[i]=sum2[i-1]+a[i];
			if(sum2[i]!=0)
			{
				cout<<2<<endl;
				cout<<1<<" "<<i<<endl;
				cout<<i+1<<" "<<n<<endl;
				break;
			}
		}
	}
	return 0;
} 




T2_Ilya and tic-tac-toe game(水题++)

在这里插入图片描述
在这里插入图片描述
——题意——
两个人在 4 ∗ 4 4*4 44的格子上下井字棋,此时正好轮到放 x x x的人下,问此人在这一回合是否能赢(三个 x x x连在一起)

——题解——
从头到尾对空位进行枚举,遍历它周围的八格,是否有 x x x,如果有,则考虑继续往这个方向查看或者查看反方向,有一个 x x x即输出“YES”。以上条件均不成立,枚举下一个空位。遍历完依然没有结果,输出“NO”。

——Code——

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
char mp[4][4];
bool check(int x,int y)
{
	int dx[8]={-1,0,1,1,1,0,-1,-1};
	int dy[8]={-1,-1,-1,0,1,1,1,0};
	for(int i=0;i<8;++i)
	{
		int x1=dx[i]+x;
		int y1=dy[i]+y;
		if(x1>=0&&x1<=3&&y1>=0&&y1<=3&&mp[x1][y1]=='x')
		{
			int x2,y2;
			x2=dx[i]+x1;
			y2=dy[i]+y1;
			if(x2>=0&&x2<=3&&y2>=0&&y2<=3&&mp[x2][y2]=='x')
				return true;
			x2=-dx[i]+x;
			y2=-dy[i]+y;
			if(x2>=0&&x2<=3&&y2>=0&&y2<=3&&mp[x2][y2]=='x')
				return true;
		}
	}
	return false;
}
int main()
{
	bool flag=false;
	for(int i=0;i<4;++i)
		scanf("%s",mp[i]);
	for(int i=0;i<4;++i)
	{
		for(int j=0;j<4;++j)
			if(mp[i][j]=='.'&&check(i,j))
			{
				cout<<"YES"<<endl;
				flag=true;
				break;
			}
		if(flag) break;
	}
	if(!flag) cout<<"NO"<<endl;
	return 0;
}




T3_Vladik and chat(动态规划 or 贪心)

在这里插入图片描述
在这里插入图片描述
——题意——
这是一道给对话匹配说话者的题目,给出 n n n个人的名字和 m m m句话,其中有一些对话已经显示说话者,说话者不会再自己说的话中出现自己的名字,并且上下相邻的话不能由同一个人说,推断出每句话的说话者,可能存在多种解,只要输出其中一种。如果无解就输出“Impossible”。

——题解——
字符串处理还是不会QAQ
设一个数组 d p [ i ] [ j ] dp[i][j] dp[i][j]表示第i句话是滴j个人说的,然后从第一句话开始依次枚举可能的说话人,并更新其它话的说话人。
最后输出答案。。。

T4_Fedor and coupons(贪心 or 二分答案)

在这里插入图片描述
在这里插入图片描述
——题意——
本题是一个区间覆盖问题。
给出 n n n个区间的左端点和右端点(完全包括),取其中 k k k个区间,要求这些区间覆盖的区域最大。

——题解——
这是我第一次接触这类题目,初看好似只有枚举这一种方法,感觉正解很玄妙,但是仔细想通后,发现思维难度并不高。
根据实验室学长给出的思路和原题解,给出两种解决办法。

1.贪心+优先队列
假设我们任意取 k k k个区间,那么这些区间覆盖的区域的左端点即所有区间左端点中最靠右的,覆盖区域的右端点是所有 k k k个区间最靠左的端点。我们希望左端点要尽量靠左,右端点尽量靠右,这样就能找到最大覆盖。所以根据左端点的位置,对 n n n个区间排序,按照从小到大依次排好。我们可以先假设前 k k k个区间就是正确答案,此时长度 l e n g t h length length为前 k k k个区间右端点的最小值和第 k k k个区间左端点(已排序)所覆盖的区域。这样求出的实际上是从第 k k k个区间左端点开始可以覆盖的最大区域,从 k k k n n n,通过这种办法就能找到最大的覆盖区域。但是,如果每次都对这某个区间的以前的所有区间的右端点进行完全排序( O ( n l o g n ) O(nlogn) O(nlogn)),会耗费大量时间,所以用一个优先队列存储 k k k个区间右端点,用于“尽量向右压缩右端点”,最后,我们得到一个最优解所处的左端点 l e f t left left,已经加上长度得出右端点 r i g h t right right,重新遍历一遍所有区间,把前 k k k个左端点小于等于 l e f t left left并且右端点大于等于 r i g h t right right的区间编号输出即可。

2.二分答案
通过考察覆盖区间的长度,我们可以看出,答案是单调的,小于等于最优解的覆盖长度是存在的,大于最优解的覆盖长度是不可行的,很明显这样就可以使用二分法找到这个临界值。二分的下限设为0,上限设为单个最长区间的长度。对区间长度进行二分,检查能否由 k k k个区间覆盖该长度。思路上一种方法类似,对所有区间的左端点排序,检查左端点加上检查的长度是否大于右端点。这么做复杂度较大,大约1e9,不如上一种方法巧妙。

下面是用贪心法的代码:
——Code——

#include<iostream>
#include<queue>
#include<algorithm>
using namespace std;
int n,k;
priority_queue<int,vector<int>,greater<int> > q;
struct lr{
	int l;
	int r;
	int id;
}a[300005];
bool cmp(lr p1,lr p2)
{
	return p1.l<p2.l;
}
int main()
{
	cin>>n>>k;
	for(int i=1;i<=n;++i)
	{
		cin>>a[i].l>>a[i].r;
		a[i].id=i;
	}
	sort(a+1,a+n+1,cmp);
	for(int i=1;i<=k;++i)
		q.push(a[i].r);
	int ans=max(0,q.top()-a[k].l+1);
	int left=a[k].l;
	for(int i=k+1;i<=n;++i)
	{
		q.push(a[i].r);
		q.pop();
		if(ans<q.top()-a[i].l+1)
		{
			ans=q.top()-a[i].l+1;
			left=a[i].l;
		}
	}
	if(ans==0)
	{
		cout<<0<<endl;
		for(int i=1;i<=k;++i)
			cout<<i<<" ";
	}
	else
	{
		cout<<ans<<endl;
		for(int i=1;i<=n;++i)
			if(a[i].l<=left&&a[i].r>=ans+left-1)
			{
				cout<<a[i].id<<" ";
				--k;
				if(k==0) break;
			}
	}
	return 0;	
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值