先说一下这几天的学习情况吧。这几天虽然vjudge上的题目不能提交啦,但是还是要以前没有看过的题补一遍。其中,做了几道dfs和bfs的题目,还看了几道bfs+bit位运算结合,以及Full Tank(最短路径+dp)最短路径变形的问题。我感觉这两个知识点掌握的不是很好,只是略懂皮毛,没有理解的很透彻,能够达到熟练应用到解决问题的地步,所以接下来几天要把这些问题尽可能消化吸收。
单源最短路径---Dijkstra算法
一.算法目的
这个算法所求的是单源最短路径,好比说你写好了Dijkstra的函数,那么只要输入点a的编号,就可算出图上每个点到这个点的距离。
时间复杂度是O(n²)
二. 算法详解
给你一个无向图。
5 6(5代表5个点,6代表5个点构成的6个临边)
1 2 5
1 3 8
2 3 1
2 4 3
4 5 7
2 5 2
Dijkstra 算法的步骤如下:
1、当到一个时间点时,图上部分的点的最短距离已确定,部分点的最短距离未确定。
2、选一个所有未确定点中离源点最近的点,把他认为成最短距离。
3、再把这个点所有出边遍历一边,更新所有的点。
注:此处以下地方不明白,以后要继续学习
写此代码的大致思路为:首先用邻接表实现dijkstra,但是这个算法有一个坏处,就是出现负权边,这个算法就炸了,要解决负权边,就要用到SPFA。
由于朴素的Dijkstra时间复杂度是n^2,但是,如果经过堆优化,Dijkstra的时间复杂度能达到nlogn,那么堆优化怎么优化?在这里,我们可以用一个优先队列,每当搜索到一个新点,扔到优先队列里面,这样每次就取队首的绝对是最优值。这样可以省去for循环。(现在不理解,只能硬性记住)
转自https://www.cnblogs.com/jason2003/p/7222182.html
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <queue>
#define in(a) a=read()
#define REP(i,k,n) for(long long i=k;i<=n;i++)
#define MAXN 10010
using namespace std;
typedef pair<long long,long long> P;
inline long long read(){
long long x=0,t=1,c;
while(!isdigit(c=getchar())) if(c=='-') t=-1;
while(isdigit(c)) x=x*10+c-'0',c=getchar();
return x*t;
}
long long n,m,s;
long long total=0,head[MAXN],nxt[MAXN<<10],to[MAXN<<10],val[MAXN<<10];
long long dis[MAXN],vis[MAXN];
priority_queue <P, vector<P>,greater<P> > Q;//优先队列优化
inline void adl(long long a,long long b,long long c){
total++;
to[total]=b;
val[total]=c;
nxt[total]=head[a];
head[a]=total;
return ;
}
inline void Dijkstra(){
REP(i,1,n) dis[i]=2147483647;
dis[s]=0;
Q.push(P(0,s));
while(!Q.empty()){
long long u=Q.top().second;//取出dis最小的点
Q.pop();//弹出
if(vis[u]) continue;
vis[u]=1;
for(long long e=head[u];e;e=nxt[e])
if(dis[to[e]]>dis[u]+val[e]){
dis[to[e]]=dis[u]+val[e];
Q.push(P(dis[to[e]],to[e]));//插入
}
}
return ;
}
int main(){
in(n),in(m),in(s);
long long a,b,c;
REP(i,1,m) in(a),in(b),in(c),adl(a,b,c);
Dijkstra();
REP(i,1,n) printf("%lld ",dis[i]);
}
位运算知识点 (位运算可用于搜索中状态不好表达时的状态压缩)
C++提供了6种位运算符来进行位运算操作:
& 按位与
| 按位或
^ 按位异或
~ 按位取反
<< 左移(左边消失,右边补0)
>> 右移(右边消失,左边补符号位)
位运算的操作数是整数类型或字符型.
按位与运算(&)
两个位同为1,结果才是1,否则为0。(如果是负数参与了运算,则负数以补码形式参与所有位运算)
例如:6的二进制是110,11的二进制是1011,那么6 & 11的结果就是2
按位与运算(&)的用法:
& 常用于二进制取位操作,例如一个数 & 1的结果就是取二进制的最末位。
按位或(|)运算
参与运算的两位只要有1个1则结果为1, 只有两位均为0结果才是0。( 0|0=0 0|1=1 1|1=1 )
2|5 即 00000010 | 0000 0101 = 00000111 因此,3|5的值得7。
按位或(|)运算用法:
|运算通常用于二进制特定位上的强制置1,例如一个数或 1的结果就是把二进制最末位强行变成1。
按位异或^运算
参与运算的两位如果相同则结果为0,参与运算的两位如果不同则结果为1。( 0^0=0 0^1=1 1^1=0 )
左移<<运算
a << b就表示把a转为二进制后左移b位(在后面添b个0)。
例如100的二进制为1100100,而110010000转成十进制是400,那么100 << 2 = 400。可以看出,a << b的值实际上就是a乘以2的b次方,因为在二进制数后添一个0就相当于该数乘以2。
左移<<运算应用:
可以用<<来定义MAXN等常量。
右移>>运算
a >> b表示二进制右移b位(去掉末b位)
右移(>>)应用举例:
1.x = x >> 1等价于x = x / 2
2.x = x >> n等价于x = x / 2^n
当a是正整数时,a>>b等价于a/(2的b次方)
当a是负整数时,a>>b并不等价与a/(2的b次方),而是等于a/(2的b次方)上取整。
如a=-9
cout<<a/2; //输出-4.
cout<<(a>>1); //输出-5.