培训补坑(day1:最短路&two-sat)
经过12天的滚粗,终于迎来了暑期培训的结尾啦QAQ
结业考才考了90分,真是对不起孙爷(孙爷请收下我的膝盖)
orz小粉兔怒D rank 1 获得小粉兔一只QAQ
由于这次12天的培训题目又比较多,算法较难,所以只能等到课程结束之后再来补坑了。
现在开始补坑(BEGIN大工程!!)
day1主要知识:最短路+two-sat
首先是最短路:
对于最短路目前我们大部分人用的就是两种算法:
一、dijkstra算法:
dij算法基于的思想就是每次从所有已拓展的节点相连的未拓展的节点取出一个dis值最小的节点加入集合,直到找到所有点的最短路
这种做法唯一的优点就是跑的比spfa快,但是这种算法不能处理负权边。而且也只是处理单源最短路问题
对于找到dis最小值的点有两种方法,第一种就是n^2暴力枚举点,但是这样太慢,所以通常我们把点放入优先队列中或者是堆中优化复杂度
然后我们贴个图来讲解一下具体做法:
一开始的时候没有一个点在集合中:

接下来一号节点入队

我们发现4号节点离1号节点最近,4号节点入队。

接下来我们发现3号节点离1号节点和4号节点最近,3号节点入队

最后二号节点入队

至此,最短路结束。
下面附上例题:
给定N个节点,M条无向边的图,求1号节点到N号节点次短路的长度
备注:次短路是长度大于最短路中最短的路径
——————————————我是分割线——————————————
这道题目显然是单源最短路问题,只不过我们我们找到一个点看能不能更新最短路,如果能就更新,否则再和次短路进行比较。注意这题最短路和次短路不能相同,所以如果一个点的dis值与最短路的值相同要跳过才可以。
下面附上代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define MN 200005
#define M 5005
using namespace std;
int head[M],d1[M],d2[M],n,m,num;
struct e{
int to,next,w;
}g[MN];
void ins(int u,int v,int val){g[++num].next=head[u];head[u]=num;g[num].to=v;g[num].w=val;}
void insw(int u,int v,int w){ins(u,v,w);ins(v,u,w);}
void swap(int &a,int &b){a^=b;b^=a;a^=b;}
struct edge{
int to,value;
edge(int to,int value):to(to),value(value) {}
friend bool operator <(edge a,edge b){
return a.value>b.value;
}
};
priority_queue<edge>q;
void dij(){
d1[]=;q.push(edge(,));
while(!q.empty()){
edge tmp=q.top();q.pop();if(tmp.value>d2[tmp.to])continue;
for(int i=head[tmp.to];i;i=g[i].next){
int dd=tmp.value+g[i].w;
if(dd==d1[g[i].to])continue;
if(dd<d1[g[i].to]){
swap(dd,d1[g[i].to]);
q.push(edge(g[i].to,d1[g[i].to]));
}
if(dd<d2[g[i].to]){
swap(dd,d2[g[i].to]);
q.push(edge(g[i].to,d2[g[i].to]));
}
}
}
}
int main(){
scanf("%d%d",&n,&m);memset(d1,0x3f,sizeof(d1));memset(d2,0x3f,sizeof(d2));
int x,y,val;
for(int i=;i<=m;i++)scanf("%d%d%d",&x,&y,&val),insw(x,y,val);
dij(),printf("%d\n",d2[n]);
return ;
}
二、spfa
spfa的原理与dij有点相像,不过它处理的是多元最短路问题,可以有多个起点,它的基础算法就是松弛操作,只要目前在队中的节点有一条边能使到达其他点的dis值更小,就更新dis值,并且让被更新的节点入队,由于一个点最多入队n次,所以最坏复杂度是O(nm),不过有一些优化可以让这个算法变得更快,这个我们下面讲。
还是刚才那张图
首先1号节点入队(假设只有一个源)

然后我们发现与一号节点相连的点有二号和四号节点。更新dis[2]=50,dis[4]=10,2,4号节点入队。1号节点出对。

然后我们看到了三号节点没有进过队,它的dis值是inf于是3号节点入队,2号节点出队

我们发现4号节点能更新2号节点和3号节点,更新完后4号节点出队

最后我们发现3号节点能够更新2号节点,更新完3号节点出队,最短路结束。
在附上代码之前讲2个优化。
1、SLF优化
因为我们看到上图进行了多次松弛操作,所以复杂度较高。
不过如果我们调换一下入队顺序,比如原来的入队顺序是2 3 4节点
如果我们改成4 3 2节点,我们就会发现没有松弛操作了。一次就得出了正解。
这就是SLF优化。如果当前找到的v(边的终点)节点的dis值比队头元素的dis值更小,那么我们就把这个点放在队头。这样能优化30%左右,并且在本图中效果最为明显。
2、循环队列优化
再讲一个不是优化的优化,就是循环队列。因为在一个队列中最多只能同时存在n(n为点的总数)个元素,所以我们可以将队列循环使用,节省空间。
下面直接附上代码
void spfa(){
memset(vis,,sizeof(vis));
for(int i=;i<=n;i++)dis[i]=inf;
dis[T]=;vis[T]=;int h=,t=;que[]=T;
while(h!=t){
int tmp=que[(++h)%=MN];
for(int i=head[tmp];i;i=g[i].next)
if(g[i^].w&&dis[g[i].to]-dis[tmp]+g[i].c>eps){
dis[g[i].to]=dis[tmp]-g[i].c;
if(!vis[g[i].to]){
vis[g[i].to]=;
if(dis[g[i].to]<dis[que[(h+)%MN]])que[h]=g[i].to,h=(h-+MN)%MN;
else que[(++t)%=MN]=g[i].to;
}
}vis[tmp]=;
}
}
接下来我们讲讲第二个大块two-set
two-sat问题就是这样:对于一个事件你只有两种选择,选,或者不选。
而且对于两个事件间还有一些约束条件,如A、B中至少要选一个。
而two-sat算法就是将这些限制条件转换为不同状态之间的边,然后通过染色来判断满足条件的情况是否存在的。
two-sat最重要的方法就是将一个点拆成2个,一个是选,一个是不选。
举个例子:
假如A、B之间我至少要选一个
那么反之,如果我A没选,那么B一定选
如果我B没选,A我一定选
那么我只要在非A与B,非B与A之间连一条单向边就好了。(非表示不选)
其他的条件同理
下面直接附上例题代码(由于是英文大家就自己理解啦。。我偷懒一下下QAQ)
#include<cstdio>
#include<cstring>
#define MN 100005
using namespace std;
int n,m,tot,x,y,num,top;
int age[MN],vis[MN*],stack[MN],head[MN*];
struct edge{
int to,next;
}g[MN*];
void ins(int u,int v){g[++num].next=head[u];head[u]=num;g[num].to=v;}
bool kind(int now){return age[now]*n<tot;}
bool dfs(int u){
if(vis[u^])return false;
if(vis[u])return true;
vis[u]=true;stack[top++]=u;
for(int i=head[u];i;i=g[i].next){
if(!dfs(g[i].to))return false;
}
return true;
}
bool t_sat(){
memset(vis,,sizeof(vis));
for(int i=;i<*n;i+=){
if(vis[i]||vis[i^])continue;
top=;
if(!dfs(i)){
while(top)vis[stack[--top]]=;
if(!dfs(i^))return false;
}
}
return true;
}
int main(){
while(scanf("%d%d",&n,&m)!=EOF){
if(n==&&m==)break;
memset(g,,sizeof(g));
memset(head,,sizeof(head));
memset(stack,,sizeof(stack));
num=tot=;
for(int i=;i<n;i++)scanf("%d",&age[i]),tot+=age[i];
for(int i=;i<=m;i++){
scanf("%d%d",&x,&y);
x--,y--;
if(kind(x)==kind(y)){
ins(x<<,y<<|);
ins(y<<,x<<|);
}
ins(x<<|,y<<);
ins(y<<|,x<<);
}
if(t_sat()){
for(int i=;i<*n;i+=){
if(!vis[i]){
printf("C\n");continue;
}
else if(kind(i/))printf("B\n");
else printf("A\n");
}
}else printf("No solution.\n");
}
}
培训补坑(day1:最短路&two-sat)的更多相关文章
- 培训补坑(day5:最小生成树+负环判断+差分约束)
补坑补坑((╯‵□′)╯︵┻━┻) 内容真的多... 一个一个来吧. 首先是最小生成树. 先讲一下生成树的定义 生成树就是在一张图上选取一些边,使得整个图上所有的点都连通. 那么我们要求的最小生成树有 ...
- 培训补坑(day8:树上倍增+树链剖分)
补坑补坑.. 其实挺不理解孙爷为什么把这两个东西放在一起讲..当时我学这一块数据结构都学了一周左右吧(超虚的) 也许孙爷以为我们是省队集训班... 好吧,虽然如此,我还是会认真写博客(保证初学者不会出 ...
- 培训补坑(day7:线段树的区间修改与运用)(day6是测试,测试题解以后补坑QAQ)
补坑咯~ 今天围绕的是一个神奇的数据结构:线段树.(感觉叫做区间树也挺科学的.) 线段树,顾名思义就是用来查找一段区间内的最大值,最小值,区间和等等元素. 那么这个线段树有什么优势呢? 比如我们要多次 ...
- 培训补坑(day4:网络流建模与二分图匹配)
补坑时间到QAQ 好吧今天讲的是网络流建模与二分图匹配... day3的网络流建模好像说的差不多了.(囧) 那就接着补点吧.. 既然昨天讲了建图思想,那今天就讲讲网络流最重要的技巧:拆点. 拆点,顾名 ...
- 培训补坑(day3:网络流&最小割)
继续补坑.. 第三天主要是网络流 首先我们先了解一下网络流的最基本的算法:dinic 这个算法的主要做法就是这样的: 在建好的网络流的图上从源点开始向汇点跑一遍BFS,然后如果一条边的流量不为0,那么 ...
- 培训补坑(day2:割点与桥+强联通分量)
补坑ing... 好吧,这是第二天. 这一天我们主要围绕的就是一个人:tarjan......创造的强联通分量算法 对于这一天的内容我不按照顺序来讲,我们先讲一讲强联通分量,然后再讲割点与桥会便于理解 ...
- 培训补坑(day10:双指针扫描+矩阵快速幂)
这是一个神奇的课题,其实我觉得用一个词来形容这个算法挺合适的:暴力. 是啊,就是循环+暴力.没什么难的... 先来看一道裸题. 那么对于这道题,显然我们的暴力算法就是枚举区间的左右端点,然后通过前缀和 ...
- 算法详解(LCA&RMQ&tarjan)补坑啦!完结撒花(。◕ˇ∀ˇ◕)
首先,众所周知,求LCA共有3种算法(树剖就不说了,太高级,以后再学..). 1.树上倍增(ST表优化) 2.RMQ&时间戳(ST表优化) 3.tarjan(离线算法)不讲..(后面补坑啦!) ...
- SQL Labs刷题补坑记录(less1-less30)
补坑加1,这几天快速刷一下sqllabs 来巩固下sql注入基础吧,也算是把很久以前没刷的过一遍,do it! 第一部分: LESS1: 直接报错,有回显的注入, http://localhost/s ...
随机推荐
- cf978E Bus Video System
The busses in Berland are equipped with a video surveillance system. The system records information ...
- WPF图片预览之移动、旋转、缩放
原文:WPF图片预览之移动.旋转.缩放 RT,这个功能比较常见,但凡涉及到图片预览的都跑不了,在说自己的实现方式前,介绍一个好用的控件:Extended.Toolkit中的Zoombox,感兴趣的同学 ...
- svn TortoiseSVN 回滚版本
原文链接: http://keenwon.com/1072.html SVN是一个版本管理工具,在工作中经常使用,尤其是多人合作开发的时候,版本管理显得更加重要.需要使用回退的场景往往都比较" ...
- DbVisualizer 解决中文乱码问题
在SQL Commander中,sql语句中如果有中文,显示是‘口口口’. 解决办法如下: 在Tools->tool Properties->General->Appearance- ...
- Eclipse 透视图(Perspective)---Eclipse教程第06课
什么是透视图? 透视图是一个包含一系列视图和内容编辑器的可视容器.默认的透视图叫 java. Eclipse 窗口可以打开多个透视图,但在同一时间只能有一个透视图处于激活状态. 用户可以在两个透视图之 ...
- 为 DirectAccess 设计 DNS 基础结构
TechNet 库Windows ServerWindows Server 2008 R2 und Windows Server 2008浏览 Windows Server 技术NetworkingD ...
- ICG-智能代码生成器.(权限控制.融入平台).(表单引擎).(最低兼容IE8)
请下拉滚动条... 代码生成器.附带客户端代码 个人平台:www.10086bank.com 界面: 1--首先是server制作界面(BS结构).直接上图: 2--点击提交生成一下文件: 各个代 ...
- HUAWEI TAG-AL00 找IMEI的过程
前几天,遇到一台华为机型,IMEI获取有问题,然后就找了一下. 以下是解决过程,权当记录一下,尽管为知笔记已经有备份了 :) 0x01: 起因 测试小哥发现,一台机型IMEI获取不全,有问题,拨号页面 ...
- thinkPHP 表单自动验证功能
昨天晚上我们老大叫我弄表单自动验证功能,愁了半天借鉴了好多官网的知识,才出来,诶,总之分享一下我自己的成果吧! thinkphp 在Model基类为我们定义了自动验证的函数和正则表达式,我们只需要在对 ...
- shell之echo and printf
#!/bin/sh _________echo___________#read name #echo "$name It is a test" #read命令从标准的输入中读取一行 ...