Codeforces Round #390 Div.2题解
不知道为啥,这次这些题给的时间还是蛮多的。。。
比赛链接
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
4∗4的格子上下井字棋,此时正好轮到放
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;
}