Codeforces Round #659 (Div. 2)

Codeforces Round #659 (Div. 2)

题号类型难度备注
A1384A Common Prefix字符串构造1
B1384B Koa and the Beach贪心3好题!
C1383A String Transformation 1暴力/图论拓扑2
E1384E. String Transformation 2弱连通,DAG4还不是很懂

官方题解传送门

1384B Koa and the Beach

题意:海岸与小岛之间的距离为n米,每米的深度不一样,为d[i], 会有海浪先涨起k秒,而后下降k秒,每秒的变化是1米,如此往复循环,Koa要从海岸去小岛,海水深度不能超过 l l l米, 而每一刻的问Koa能否从海岸去小岛。

比如 d[i] 为 1,2,3,k=2

第0秒时,Koa在岸上,这时 海面的高度为 1 2 3

第1秒时,海面的高度为 2 3 4

第2秒时,海面的高度为 3 4 5

第3秒时,海面的高度为 2 3 4

第4秒时,海面的高度为 2 3 4

思路

<一> 贪心 (官方题解)

对于深度为 d[p]+k<=l 的位置p,是绝对安全位置,意味着可以随便调整出发时间。

2个安全位置之间只能经历 海水深度从高 下降再上升的过程,也就是 先k>n,而后k<0。

p a , p b p_a,p_b pa,pb 为2个相邻的安全位置,什么时候从 p a p_a pa 出发呢,在达到t=2k(或者也可以说为0时,取模),也就是海浪高度为0时,海浪的高度是递减的,因此 在 p 1 p_1 p1 时,如果安全了,可以一直等待到下一个位置也安全了再走。这样就能使得在海浪高度变为0时,走最多的路,而后面海浪高度就要上升了,这是就无力回天,只能判断海浪高度+d[i] 是否会大于l了。

#include <bits/stdc++.h>

using namespace std;

int main()
{
	ios_base::sync_with_stdio(0), cin.tie(0);

	int test;
	cin >> test;
	while (test--)
	{
		int n, k, l;
		cin >> n >> k >> l;
		vector<int> d(n+1), safe = { 0 };
		for (int i = 1; i <= n; ++i)
		{
			cin >> d[i];
			if (d[i] + k <= l)
				safe.push_back(i);
		}

		safe.push_back(n+1);
		bool ok = true;
		for (size_t i = 1; i < safe.size() && ok; ++i)
		{
			int tide = k; bool down = true;
			for (int j = safe[i-1] + 1; j < safe[i]; ++j)
			{
				tide += down ? -1 : +1;

				if (down) tide -= max(0, d[j] + tide - l);
				if (tide < 0 || d[j] + tide > l) { ok = false; break; }

				if (tide == 0) down = false;
			}
		}

		if (ok) cout << "Yes\n";
		else cout << "No\n";
	}

	return 0;
}
1383A String Transformation 1

题意 :有2个字符串,A和B,把A中某些相同的字符统一变成另一个字符是一个操作,且只能变成比它字典序大的小写字母,问实现A变成B的最少操作数。

比如 A=“aabd”, B=“bccd”

step1: aabd ⇒ \Rightarrow bbbd

step2: bbbd ⇒ \Rightarrow bccd

思路

<一> 暴力

可见其实只能把A中的字符变大, 如果A中是‘a’,对应的有‘b’,有‘c’等等 ,那么必定要把’a‘变成’b‘,但是不一定要把’a‘变成’c’,因为可能后面有’b‘变成’c‘的情况,而现在把’a‘变成’b‘转换成’b‘变成’c‘就是免费的顺风车。

#include "bits/stdc++.h"
using namespace  std;
const int MAXN=1E5+10;
char ss1[MAXN],ss2[MAXN];
bool turn[22][22];
int main()
{
//    freopen("in.text","r",stdin);
    int cas;
    scanf("%d",&cas);
    while (cas--)
    {
        int n,suc=1;
        memset(turn,0,sizeof(turn));
        scanf("%d %s %s",&n,ss1,ss2);
        for(int i=0;i<n;++i)
        {
            if(ss1[i]==ss2[i])continue;
            if(ss1[i]>ss2[i]){suc=0;break;}
            turn[ss1[i]-'a'][ss2[i]-'a']=1;
        }

        int ans=0;
        for(int i=0;i<20&&suc;++i)
            for(int d=i+1;d<20;++d)
            {
                if(!turn[i][d])continue;
                ++ans;
                // 接下来就是免费顺风车
                for(int g=i+1;g<20;++g)
                {
                    turn[d][g]+=turn[i][g];
                    turn[i][g]=0;
                }
            }

        if(!suc)ans=-1;
        printf("%d\n",ans);
    }
    return 0;
}

<二> 图论解法

回到开头的例子,比如 A=“aabd”, B=“bccd”

step1: aabd ⇒ \Rightarrow bbbd

step2: bbbd ⇒ \Rightarrow bccd

那就是 a变成b,b变成c,把a,b,c看做节点,那么这种turn转换关系就是边,由于它要求只能由小变成大,那么必定是无环的拓扑。类似如下的

mark

这样的话,可见对于有m个点的拓扑,边的数量最少为m-1个,

因此可以通过dfs找到建立的图中有cnt个连通体(拓扑集),那么最优的情况(最少边数)就是20-cnt。

#include "bits/stdc++.h"
using namespace  std;
const int MAXN=1E5+10;
char ss1[MAXN],ss2[MAXN];
bool mp[22][22];
bool vis[20];
void dfs(int a){
    vis[a]=1;
    for(int i=0;i<20;++i)if(!vis[i]&&mp[a][i]) dfs(i);
}
int main()
{
//    freopen("in.text","r",stdin);
    int cas;
    scanf("%d",&cas);
    while (cas--)
    {
        int n,suc=1;
        memset(mp,0,sizeof(mp));
        memset(vis,0,sizeof(vis));
        scanf("%d %s %s",&n,ss1,ss2);
        for(int i=0;i<n;++i)
        {
            if(ss1[i]==ss2[i])continue;
            if(ss1[i]>ss2[i]){suc=0;break;}
            mp[ss1[i]-'a'][ss2[i]-'a']=1;
            mp[ss2[i]-'a'][ss1[i]-'a']=1;

        }
        int cnt=0,ans;
        for(int i=0;i<20&&suc;++i)if(!vis[i])++cnt,dfs(i);
        if(!suc)ans=-1;else ans=20-cnt;
        printf("%d\n",ans);
    }
    return 0;
}

这个图论角度的思路是看官方题解给的,官方题解看不太懂,后来看这个评论明白了。

Solution of div1C only gives the answer but doesn’t really explain how one might come up with that in the first place, so I’ll try.

First observation is having lots of letters in the first string is bad for us, so we want to join letters as much as possible. That is, suppose the solution does operations A->B, followed by A->C. We could have changed all As to Bs in the first operation to do A->B, B->C instead. B->A followed by C->A can also be replaced by B->C, C->A.

From this, we might intuit that the solution consists of one very long chain in which we have a “snowball” collecting all original letters into the same letter.

If the graph has no cycles, then such a snowball can simply follow the topological sort of the DAG (this is div1A). If there are cycles, then some vertices will be visited more than once. If you know that a vertex is going to be visited more than once anyway, you might as well put it as the first and last vertices in the order, since this guarantees that vertex can reach/is reachable by all others.

What about the vertices you only visit once? Clearly there cannot be any cycles between them, so they are a DAG (and as we know any DAG can be solved with the topological sort). So the solution looks like: visit some set of vertices S, do topological sort on the rest, visit S again. To minimize the number of operations we want the DAG to be the maximum possible.

1384E. String Transformation 2

题意:有2个字符串,A和B,把A中某些相同的小写字母统一变成另一个小写字母是一个操作,问实现A变成B的最少操作数。

基础知识

在无向图中, 若从顶点v1到顶点v2有路径, 则称顶点v1与v2是连通的。如果图中任意一对顶点都是连通的,则称此图是连通图

强连通和弱连通的概念只在有向图中存在。

强连通图:在有向图中, 若对于每一对顶点v1和v2, 都存在一条从v1到v2和从v2到v1的路径,则称此图是强连通图。

弱连通图:将有向图的所有的有向边替换为无向边,所得到的图称为原图的基图。如果一个有向图的基图是连通图,则有向图是弱连通图。

官方题解

The only difference between this problem and the previous problem is that the underlying graph might have cycles.
Each weakly connected component can be solved independently and the answer is 2⋅n−|LDAG|−1. where n is the number of nodes in the component and |LDAG| is the size of the largest Directed Acyclic Graph.
The largest directed acyclic graph can be computed using dynamic programming in O ( 2 n ⋅ n ) O(2^n⋅n) O(2nn).

For every node u store a mask reach[u] with all nodes it can reach directly. Then go through every possible mask from 1 t o 2 n − 1 1 to 2^n−1 1to2n1 and check whether this set of nodes is acyclic or not. It is acyclic if there exists at least one node u (the last node in one topological order) such that the set without this node is acyclic and this node doesn’t reach any other node in this set:

is_dag[0] = 1
for mask in 1..2^n-1
   for u in mask:
      is_dag[mask] |= is_dag[mask ^ (1 « u)] && ((mask & reach[u]) == 0))

Proof by @eatmore:

Lemma 1: Suppose that there is a weakly connected component with n vertices, and there is a solution with k edges. Then, the size of the largest DAG is at least 2⋅n−1−k .

Proof: Let’s keep track of current weakly connected components. Also, in each of the components, let’s keep track of some DAG. Initially, each vertex is in a separate component, and each DAG consists of a single vertex. So, there are n components, and the total size of all DAGs is n.

Processing an edge (u,v):

  1. If u and v are in the same component: if v is in the DAG, remove it. Number of components is unchanged, the total size of all DAGs is decreased by at most 1.
  2. If u and v are in different components, join the components. Concatenate the DAGs (DAG of u’s component comes before DAG of v’s component ). Number of components decreases by 1, the total size of all DAGs is unchanged.

At the end, the number of components becomes 1, so n−1 edges are used to decrease the number of components. The remaining kn+1 edges could decrease the size of DAGs, so the final size is at least n−(kn+1)=2⋅n−1−k

From lemma 1 we know that k>=2⋅n−1−|DAG|, then k is minimized when the size of the final DAG is maximized.
Time complexity: O(|A|+|B|+2nn) per test case

我的理解

这个和上题“ String Transformation 2”的区别就是 可以变成比它字典序小的,因此形成的连通体中可能有环,也就是是说要形成的连通体可能是弱连通图。如下图

mark

而题解中给出一个结论:一个有n个顶点,k条边的弱连通分量,k>=2*n-1-|DAG|,因此想要得到最大的k,就要找到|LDAG|。证明看题解把。 推广到整个包含多个连通图的整体,就是 k>=2* n-cnt- |LDAG|

接下来就讲怎么找。

那么就要从这个连通体中找出一个DAG(有向无环图),或者而说是上题所说的拓扑序,

而若图C是一个DAG,那么必定图C中必定存在一个节点满足以下2个条件

​ (1)从图C中去掉它,图C也是一个DAG图,也就是说也是无环的。

​ (2)该节点不能到达图C中其他的任何1个节点。

#include<cstdio>
#include "bits/stdc++.h"
#include<iostream>
using namespace std;
const int N=100010,M=20;
int T,n,reach[M],is_DAG[1<<M],cnt,ans,mp[M][M],vis[M];
char S1[N],S2[N];
void dfs(int u){
    vis[u]=1;
    for(int i=0;i<20;++i)if(mp[u][i]&&!vis[i])dfs(i);
}
int main(){
//	freopen("in.text","r",stdin);
    scanf("%d",&T);
    while(T--){
        scanf("%d%s%s",&n,S1,S2);
        memset(mp,0,sizeof(mp));
        memset(reach,0,sizeof(reach));
        for(int u,v,i=0;i<n;++i)if(S1[i]!=S2[i]){
                u=S1[i]-'a',v=S2[i]-'a';
                reach[u]|=1<<v;
                mp[u][v]=mp[v][u]=1;
            }
        ans=cnt=0;
        for(int i=0;i<20;++i)vis[i]=0;
        for(int i=0;i<20;++i)if(!vis[i]){
                cnt++;dfs(i);
            }
        is_DAG[0]=1;
        for(int i=1;i<(1<<20);++i){
            is_DAG[i]=0;
            for(int j=0;j<20;++j)  //去掉存在该弱连通图中的某个点
                if(i&(1<<j)){
                    is_DAG[i] |= is_DAG[i^(1<<j)] && ((reach[j]&i)==0);   //判断符合DAG的2个判断标准
                    if(is_DAG[i])break;
                }
            if(is_DAG[i]) ans=max(__builtin_popcount(i),ans);    //__builtin_popcount(i)统计有多少个1,得到最大DAG图的大小
        }
        printf("%d\n",40-cnt-ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是Mally呀!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值