Codeforces Round #605 (Div. 3) 补题(E最短路/多源bfs、Fdp+输出路径)

E. Nearest Opposite Parity(最短路)

n(n<=2e5)个数的数组a[],第i个数可以跳到i-a[i]或i+a[i],

若a[i]跳到一个与其奇偶性不同的a[j]即可认为对于i来说跳跃终止。

对于每个i,求最短的跳跃终止的距离。

 

建反图,i-a[i]连向i代价1,i+a[i]连向i代价1,所有偶点连超级偶点代价0,所有奇点连超级奇点代价0

这样本来所有奇点到超级偶点的最短路,可认为是超级偶点到所有奇点的最短路

同理,跑一次超级奇点到所有偶点的最短路,粘一下dij板子就过了

dfs由于有环的更新顺序故不可行,也可以不实际建出超级源点,

考虑多源bfs,补一下多源bfs的做法,感觉很简洁

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int n,a[N],dis[N],ans[N];
vector<int>e[N];
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;++i)
	{
		scanf("%d",&a[i]);
		if(i-a[i]>=1)e[i-a[i]].push_back(i);
		if(i+a[i]<=n)e[i+a[i]].push_back(i);
	}
	for(int d=0;d<2;++d)
	{
		memset(dis,-1,sizeof dis);
		queue<int>q;
		for(int i=1;i<=n;++i)
		{
			if((a[i]&1)==d)
			{
				dis[i]=0;
				q.push(i);
			}
		}
		while(!q.empty())
		{
			int u=q.front();
			q.pop();
			for(int v:e[u])
			{
				if(dis[v]==-1)
				{
					dis[v]=dis[u]+1;
					q.push(v);
				}
			}
		}
		for(int i=1;i<=n;++i)
		{
			if((a[i]&1)!=d)
			{
				ans[i]=dis[i];
			}
		}
	}
	for(int i=1;i<=n;++i)
	printf("%d%c",ans[i]," \n"[i==n]);
	return 0;
} 

F. Two Bracket Sequences(bfs+dp)

给你两个不超过200的不一定合法的括号序列

输出一个最短的合法的括号串,使得这两个括号序列都是这个串的子序列

 

子序列匹配显然越前出现越好,所以dp2选1去枚举当前选左括号还是选右括号,贪心地和当前位置对应匹配

dp[i][j][k]代表当前该匹配s的第i个字母 该匹配t的第j个字母 且有k个左括号未匹配(左括号比右括号多k个)

状态数降到200*200*400,pre[i][j][k]直接暴力记录前驱是哪个三元组即可,空间足够

最后答案即为dp[n][m][0]的值,然后根据前驱倒序输出路径

#include<bits/stdc++.h>
using namespace std;
const int N=205,M=405;
int n,m,d[N][N][M],cnt;
char s[N],t[N],res[M];
struct pos
{
	int x,y,z;
}pre[N][N][M],now,las;
void solve()
{
	int x,y,z,xx,yy,zz;
	memset(d,-1,sizeof d);
	queue<pos>q;
	q.push({0,0,0});
	d[0][0][0]=0;
	while(!q.empty())
	{
		now=q.front();
		q.pop();
		x=now.x,y=now.y,z=now.z;
		for(char c:{'(',')'})
		{
			xx=x,yy=y,zz=z;
			if(xx<n&&s[xx]==c)xx++;
			if(yy<m&&t[yy]==c)yy++;
			if(c=='(')zz++;
			else zz--;
			if(zz<0||zz>=M)continue;
			if(d[xx][yy][zz]==-1)
			{
				d[xx][yy][zz]=d[x][y][z]+1;
				pre[xx][yy][zz]={x,y,z};
				if(xx==n&&yy==m&&zz==0)
				{
					las={xx,yy,zz};
					return;
				}
				q.push({xx,yy,zz});
			}
		}
	}
}
int main()
{
	scanf("%s%s",s,t);
	n=strlen(s);m=strlen(t);
	solve();
	int x,y,z;
	while(1)
	{
		now=las;
		x=now.x,y=now.y,z=now.z;
		if(x+y+z==0)break;
		if(pre[x][y][z].z<z)res[cnt++]='(';
		else res[cnt++]=')';
		las=pre[x][y][z];
	}
	reverse(res,res+cnt);
	res[cnt++]='\0';
	printf("%s\n",res);
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Code92007

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

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

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

打赏作者

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

抵扣说明:

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

余额充值