首页 > 其他分享 >CF1694E Keshi in Search of AmShZ#800(div.2)

CF1694E Keshi in Search of AmShZ#800(div.2)

时间:2022-09-19 21:58:13浏览次数:101  
标签:Search dist idx int Keshi AmShZ include dis

题目链接

https://codeforces.com/contest/1694/problem/E

题意简述

\(Keshi\) 准备从城市 \(1\) 出发,前往 \(AmShZ\) 所在的城市 \(n\) .这里一共有 \(n\) 个城市 ,编号 \(1 \sim n\),有 \(m\) 条有向道路. \(Keshi\) 不知道城市的路线,他需要听从 \(AmShZ\) 的指挥.
每一天的开始, \(AmShZ\) 告诉 \(Keshi\) 以下两条信息之一

  • 告诉他一个阻挡的道路 ,之后 \(Keshi\) 将永远不会走这条道路,并且他会留在他当前所在的城市一天
  • 让 \(Keshi\) 开始移动 , \(Keshi\) 将会随机选择一条路线移动到附近的一个可以到达的相邻的城市.如果无路可走,他将留在当前城市

现在告诉你城市和道路的数量,以及各个道路的起始点和终点
请你告诉他们最少需要多少天能保证 \(Keshi\) 一定可以到达 \(AmShZ\) 所在的城市

样例

点击查看样例

image

分析

为了避免引起混淆提前说一下:

一条由 \(a\) \(\rightarrow\) \(b\) 的边,下称 \(a\) 是这条边的起始点 ,\(b\)是这条边的结束点

图中第一次走的起始点称为起点,目的地称为终点.

这个题一开始想的就是从起点开始走,走任何一条边的一次的花费等于这条边的起始点的出度\(-1\) (封堵其他路径的花费)+ \(1\) \((\) 从起始点走到结束点花费一天 \()\).用 \(dist[i]\) 记录 从起点 \(1\) 走到 \(i\) 的最小花费天数,然后愉快的WA16了.
想了很久,搜了很多题解,仍然没想出哪错了..

其实走的过程中有些路径是不用删的,而这种情况是我们从前往后走所无法考虑到(或者说无法判断出)的.

看下面这张图:

image

从 \(1\) 到 \(n\) 的距离最小很显然是 \(2\) ,一条边都不用删 .而如果采用我们刚才想的那个删边策略,从 \(1\) 出发走到下一个点 ,无论是哪个,都要删掉两个边,白白的浪费了 \(2\) 天,最后结果是 \(3\) ,比正确答案偏大了.

因此,我们删边的时候还应该考虑到当前边的结束点到结点 \(n\) 的距离长短以确保不会出现浪费次数的情况.正着走显然是无法解决的.那么我们看看反向建图能不能解决这个问题.

从点 \(n\) 开始往前走 ,每一次取出距离点 \(n\) 最近的点 \(x\) ,用它来更新与 \(x\) 相邻的点到 \(n\) 的距离 .用 \(dis[i]\) 表示点 \(i\) 走到点 \(n\) 最小的无论怎么走一定能走到 \(n\) 的所需花费的天数

考虑下面这种情况

image

中间这些点到 \(n\) 的距离分别为 $1\ 3\ 8\ $,那么 \(dis[v]\) 就要被这三个点更新

(为了方便起见我们把中间这三个点从下往上编号 \(2\ 3\ 4\))

首先对于 \(dis=1\)的 \(2\) 号点来说, 想用 \(dis[2]\) 来更新 \(dis[v]\) 的话,为了保证 \(v\) 无论怎么走一定能在这个步数内到 \(n\) ,需要把与 \(v\) 相邻的 \(dis\) 比 \(dis[2]\)大的点所连的边删掉. 那么 从 \(2\) 走到 \(v\) 的花费天数 \(dist=dis[2]+din[v]-1+1\)(即使从 \(2\) 到 \(v\) 有重边也不会影响答案的正确性,因为我们对重边也会全都遍历到,那么 din[y] 也会随之改变 ,下面会讲到)

对于 \(dis=3\)的 \(3\) 号点来说, 想用 \(dis[3]\) 来更新 \(dis[v]\) 的话,为了保证 \(v\) 无论怎么走一定能在这个步数内到 \(n\) ,需要把与 \(v\) 相邻的 \(dis\) 比 \(dis[2]\)大的点所连的边删掉,即\(3\ 4\) 号点, 那么 从 \(2\) 走到 \(v\) 的花费天数 \(dist=dis[2]+din[v]-2+1\) (结点 \(2\) 的dis更小,不用删结点 \(2\) 的边 ,所以花费天数比 \(2\) 号点少一天)

那么我们如果当前结点能走到 \(v\) 时,由于我们使用了优先队列,显然每次取出的点要么无法到达 \(v\) ,要么是距离 \(v\) 是当前最近的(当然肯定也是未访问过的点),更新完 \(dis[v]\) ,再让 \(dis[v]--\) , 就能保证与 \(v\) 相连的点更新 \(dis[v]\) 时的时间花费是合理的.就直接是\(dis[v]=min(dis[v],dis[u]+din[i])\) 不用额外的减什么东西了.

代码

点击查看第一次正向建图遍历的错误代码
#include<stdio.h>
#include<iostream>
#include<cstdlib>
#include<string.h>
#include<algorithm>
#include<vector>
#include<unordered_map>
#include<queue>
using namespace std;
typedef long long LL;
const int N=2e5+10;
int e[N],ne[N],idx,h[N];
int dout[N],din[N];
unordered_map<int,int>mp[N];
typedef pair<LL,int>PII;
priority_queue<PII,vector<PII>,greater<PII> > q;
void add(int a,int b)
{
	e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
int n,m;
LL dis[N];
int vis[N];
LL dijkstra()
{
	memset(dis,0x4f,sizeof dis);
	dis[1]=0;
	q.push({0,1});
	while(q.size())
	{
		auto t=q.top();
		q.pop();
		int i=t.second;
		
		if(vis[i])continue;
		
		vis[i]=1;
		for(int j=h[i];j!=-1;j=ne[j])
		{
			int k=e[j];
			LL dist=dis[i]+dout[i];
			if(mp[i][k]>1)
			{
				dist-=(mp[i][k]-1);
			}
			if(dis[k]>dist)
			{
				dis[k]=dist;
				q.push({dis[k],k});
			}
		}
	}
	return dis[n];
}
int main()
{
	//freopen("uva.txt","r",stdin);
	memset(h,-1,sizeof h);
	
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		int a,b;
		scanf("%d%d",&a,&b);
		
		if(mp[a].find(b)==mp[a].end())
		{
			mp[a][b]=1;
			add(a,b);
		}else mp[a][b]++;
		dout[a]++;
		
	}
	
	printf("%lld\n",dijkstra());
	return 0;
}
点击查看AC代码
#include<stdio.h>
#include<iostream>
#include<cstdlib>
#include<string.h>
#include<algorithm>
#include<vector>
#include<unordered_map>
#include<queue>
using namespace std;
typedef long long LL;
const int N=2e5+10;
int e[N],ne[N],idx,h[N];
int dout[N],din[N];
unordered_map<int,int>mp[N];
typedef pair<LL,int>PII;
priority_queue<PII,vector<PII>,greater<PII> > q;
void add(int a,int b)
{
	e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
int n,m;
LL dis[N];
int vis[N];
LL dijkstra()
{
	memset(dis,0x4f,sizeof dis);
	dis[n]=0;
	q.push({0,n});
	while(q.size())
	{
		auto t=q.top();
		q.pop();
		int i=t.second;
		
		if(vis[i])continue;
		
		vis[i]=1;
		for(int j=h[i];j!=-1;j=ne[j])
		{
			int k=e[j];
			LL dist=dis[i]+din[k];
			if(dis[k]>dist)
			{
				dis[k]=dist;
				q.push({dist,k});
			}
			din[k]--;
		}
	}
	return dis[1];
}
int main()
{
	memset(h,-1,sizeof h);
	
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		int a,b;
		scanf("%d%d",&a,&b);
		add(b,a);
		din[a]++;
	}
	
	printf("%lld\n",dijkstra());
	return 0;
}

标签:Search,dist,idx,int,Keshi,AmShZ,include,dis
From: https://www.cnblogs.com/oijueshi/p/cf1694e.html

相关文章

  • elasticsearch集群搭建
    elasticsearch集群搭建 我们会在单机上利用docker容器运行多个es实例来模拟es集群。不过生产环境推荐大家每一台服务节点仅部署一个es的实例。部署es集群可以直接使用d......
  • SpringBoot中ElaticSearch工具类-附源码
    importorg.apache.http.HttpHost;importorg.elasticsearch.client.RestClient;importorg.elasticsearch.client.RestHighLevelClient;importorg.springframework.c......
  • 文献学习-Better Decision Heuristics in CDCL through Local Search and Target Phas
       OurfirstcontributionistomaximizeinalocalsearchfashiontheassignmenttrailinCDCL,bystickingtoandextendingpromisingassignmentsv......
  • ElasticSearch聚合之管道聚合(Pipeline Aggregation)
    管道聚合让上一步聚合的结果作为下一个聚合的输入,类似stream()流的操作,当不上终结操作时,每次操作的流都作为下次操作的输入管道类型有很多种不同类型,每种类型都与其他聚......
  • ElasticSearch让的分布式系统架构设计
    注:本文摘自:https://mp.weixin.qq.com/s/dOTF9BVdySiwtkUrNg-gEA分布式系统类型多,涉及面非常广,不同类型的系统有不同的特点,批量计算和实时计算就差别非常大。这篇文章中,重......
  • 干货 | Elasticsearch Java 客户端演进历史和选型指南
    1、Elasticsearchjava客户端为什么要选型?Elasticsearch官方提供了很多版本的Java客户端,包含但不限于:Transport客户端JavaREST客户端LowLevelREST客户端Hi......
  • ElasticSearch进阶:各种ES查询在Java中的实现
    注:本文摘自:https://mp.weixin.qq.com/s/7vEy-vN8JV3o6sAh6HFohA   本文基于elasticsearch7.13.2版本,es从7.0以后,发生了很大的更新。7.3以后,已经不推荐使用Transpo......
  • 深入理解全文搜索引擎 Elasticsearch
    注:本文摘抄自:https://mp.weixin.qq.com/s/Q-QV86XntKniQlMohIaexQ生活中的数据搜索引擎是对数据的检索,所以我们先从生活中的数据说起。我们生活中的数据总体分为两种:结......
  • Elasticsearch和Solr的区别
    1、基于Lucene开发他们底层都是基于Lucene开发,使用了Lucene的倒排索引实现的2、解决IO阻塞性能solr在实时建立索引的时候产生的IO阻塞查询性能会比ES差一些3、是否......
  • 98.validate-binary-search-tree 验证二叉搜索树
    二叉搜索树定义:节点左子树只包含小于当前节点的数;节点右子树只包含大于当前节点的数;所有左子树和右子树自身必须也是二叉搜索树。实际上,若中序遍历二叉搜索树,所得序列......