比赛链接:Dashboard - Educational Codeforces Round 163 (Rated for Div. 2) - Codeforces

C

题意:有一个 $2\times n$ 的网格,每个格子有一个指向左或右的箭头。有一机器人从 $(1,1)$ 开始,每次的操作如下:先向上下左右走到任一个网格(不能超出地图),然后根据该格箭头走一步。问能否走到 $(2,n)$ 。

dfs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include<bits/stdc++.h>
using namespace std;

int mp[3][200005];
int v[3][200005];
int n;
bool flag=0;
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};

bool check(int x,int y)
{
if(x<1 || x>2 || y<1 ||y>n)return 0;
return 1;
}

void dfs(int x,int y)
{
if(v[x][y])
{
return;
}
if(x==2 && y==n)
{
flag=1;
return ;
}

v[x][y]=1;
for(int i=0;i<4;i++)
{
int nx=x+dx[i];
int ny=y+dy[i];
if(!check(nx,ny))continue;
ny+=mp[nx][ny];
dfs(nx,ny);
if(flag)return ;
}
}

void solve()
{
cin>>n;
flag=0;
for(int i=1;i<=n;i++)v[1][i]=0,v[2][i]=0;
for(int i=1;i<=2;i++)
for(int j=1;j<=n;j++)
{
char c;
cin>>c;
if(c=='>')mp[i][j]=1;
else mp[i][j]=-1;
}

dfs(1,1);
if(flag)cout<<"YES\n";
else cout<<"NO\n";
}

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

bfs/dfs


D

给一个由小写字母和问号组成的字符串 $s$,问号可以表示任何字母。求最大的偶数长度的连续子串,其前半部分与后半部分相同。$n\leq 5000$

一开始考虑区间dp,发现不能转移,然后换了思路。。

枚举一半的子串长度 $len$,处理某一位置后 $len$ 的位置是否与此相同,然后找连续相同的元素,若连续超过 $len$ 那么长度为 $2\times len$ 的子串存在。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include<bits/stdc++.h>
using namespace std;

void solve()
{
char s[5005];
cin>>s+1;
int n=strlen(s+1);

int ans=0;
for(int len=1;len*2<=n;len++)
{
int q[n+2]={0};
for(int i=1;i+len<=n;i++)
{
if(s[i]==s[i+len] || s[i]=='?' || s[i+len]=='?')q[i]=1;
else q[i]=0;
}

int cnt=0;
for(int i=1;i<=n;i++)
{
if(q[i])cnt++;
else
{
if(cnt>=len)
{
ans=max(ans,len*2);
}
cnt=0;
}
}
}

cout<<ans<<endl;

}

int main()
{
int t;
cin>>t;
while(t--)solve();
// system("pause");
return 0;
}

strings


E

题意:给两个整数 $n$ 和 $k$ ,$n$ 个顶点上有一个图。现在需要给每一个顶点分配一个 $1-n$ 整数 $a_i$ ,对于两个顶点,如果 $|i - j| + |a_i - a_j| ≤ k$ ,那么在这两个点之间添加一条边。现在需要把这个图分割成尽量少的群,对于每一个群,任两个顶点之间存在一条边。求每个顶点分配的整数、最小群的数量以及每个顶点属于哪一个群。

好妙的贪心

  1. 不要从整个图开始考虑,从每个群开始考虑。在限制条件中 $|i - j|$ 是固定的因此要使得满足条件,选择序号连续的点构成一个群是最优的。
  2. 对于一个群,分配整数时,同样选择一段连续的整数进行分配是最优的。因此从1开始依次选择连续的整数段分配给对应的群。
  3. 考虑大小为 $n$ 的群能否满足 $|i - j| + |a_i - a_j| ≤ n$ 。假设序号为 $1-n$ 的点对应整数 $1-n$ ,考虑将数列对半分,即对序号为 $1-\lceil \frac{n}{2}\rceil$ 的点,依次分配 $\lceil \frac{n}{2}\rceil,\lceil \frac{n}{2}\rceil-1,…,2,1$ ,对序号为 $\lceil \frac{n}{2}\rceil+1-n$ 的点,依次分配 $n,n-1,…,\lceil \frac{n}{2}\rceil+2,\lceil \frac{n}{2}\rceil+1$ 。这样保证每个半区内部的最大值为 $n-2$ ,不同半区的任两个元素的差均为 $n$ 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include<bits/stdc++.h>
using namespace std;

void solve()
{
int n,k;
cin>>n>>k;
int a[k+5]={0};
int ans[n+5]={0};
int c[n+5]={0};

int tot=(n-1)/k+1;

for(int i=1;i<=(k-1)/2+1;i++)
{
a[i]=(k-1)/2+1-i+1;
}

for(int i=(k-1)/2+2;i<=k;i++)
{
a[i]=k-i+(k-1)/2+2;
}

for(int i=1;i<=tot;i++)
{
if(i!=tot || i==tot && tot*k==n)
{
for(int j=1;j<=k;j++)
{
ans[k*(i-1)+j]=a[j]+k*(i-1);
c[k*(i-1)+j]=i;
}
}


else
{
int r=n%k;
int b=n/k*k;
for(int j=1;j<=(r-1)/2+1;j++)
{
ans[b+j]=(r-1)/2+1-j+1+b;
c[b+j]=tot;
}

for(int j=(r-1)/2+2;j<=r;j++)
{
ans[b+j]=r-j+(r-1)/2+2+b;
c[b+j]=tot;
}
}
}

for(int i=1;i<=n;i++)cout<<ans[i]<<" ";cout<<endl;
cout<<tot<<endl;
for(int i=1;i<=n;i++)cout<<c[i]<<" ";cout<<endl;
}

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

greedy