培训补坑(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 ...
随机推荐
- opencv中对图像的像素操作
1.对灰度图像的像素操作: #include<iostream> #include<opencv2/opencv.hpp> using namespace std; using ...
- contest0 from codechef
A CodeChef - KSPHERES 中文题意 Mandarin Chinese Eugene has a sequence of upper hemispheres and another ...
- salt 通信及其安全
salt 通信及其安全 模型架构 server-agent通信模型: server就是salt master; agent就是salt-minion salt也可以作为一个单点服务器管理工具使用,或者 ...
- Best Practices in JavaScript
Some items you should konw : Graceful degradation : ensuring that your web pages still work without ...
- idea无法新建maven项目
之前用的都是eclipse,自从4月底入职新公司后,接触到了idea. 然后自己的电脑上也安装了idea,不过一直都没用,直到昨天打算开起来使用一下. 之后就是想新建一个maven项目,发现死活也新建 ...
- oracle(sql)基础篇系列(三)——数据维护语句、数据定义语句、伪列
DML语句 insert 向表中插入新的记录 --三种插入方式 --(1)不写字段的名字,直接按照字段的顺序把值逐个往里插 insert into dept2 values(50,'DANAME',' ...
- Eclipse 菜单---Eclipse教程第04课
Eclipse 查看的菜单栏通常包含以下几个菜单: File 菜单 Edit 菜单 Navigate 菜单 Search 菜单 Project 菜单 Run 菜单 Window 菜单 Help 菜单 ...
- php中utf-8转unicode
public function utf8_unicode($str) { $unicode = array(); $values = array(); $lookingFor = 1; for ($i ...
- CocosCreator设置启动场景
刚开始接触CocosCreator,在调试时,如果有多个场景,不知道如何设置将某个指定的场景设置为启动场景,折腾了一圈,找到了设置的地方, 记录一下. 点击项目->项目设置 在预览运 ...
- ajax向Asp.NET后端传递数组型数据
近日,在开发一个组件的过程中,需要通过Ajax对象向Asp.NET后端传递一个比较复杂的表单,表单中的一个字段是数组类型,我能想到的办法是用JSON.stringify将前端的数组对象序列化成字符串, ...