先来点涩图给xd助助兴⭐♥
单源最短路径
先来点涩图给xd助助兴??
Dijkstra算法
基于贪心思想,适合于非负权图
void dijkstra()
{
memset(dis, 0x3f3f3f3f, sizeof(dis));
dis[1] = 0;
for (int i = 1; i < n; i++)
{
int x = 0;
for (int j = 1; j <= n; j++)
{
if (!vis[j] && (x == 0 || dis[j] < dis[x]))
x = j;
vis[x] = 1;
for (int j = 1; j <= n; j++)
dis[j] = min(dis[j], dis[x] + a[x][j]);
}
}
}
堆优化的dijkstra算法
int dis[N], nex[N], to[N], val[N], head[N], ans[N];
bool vis[N];
inline void add(int u, int v, int w) //链式向前星
{
to[++cnt] = v; //终点
val[cnt] = w; //权值
nex[cnt] = head[u]; //nex表示与这个边起点相同的上一条边的编号
head[u] = cnt;
}
void dijkstra()
{
priority_queue<pii, vector<pii>, greater<pii>> p; //定义小根堆
memset(dis,0x3f3f3f3f,sizeof(dis));
memset(vis,0,sizeof(vis));
dis[1] = 0;
p.push(make_pair(0, 1)); //第一维是dis值,第二维是结点编号
while (!p.empty())
{
int x = p.top().second; //x为dis最小的,标记结点
p.pop();
if (vis[x])
continue;
vis[x] = 1;
for (int i = head[x]; i; i = nex[i]) //链式向前星的遍历,遍历以x为起点的边
{
int y = to[i], z = val[i];
if (dis[y] > dis[x] + z) //松弛边
{
dis[y] = dis[x] + z;
p.push(make_pair(dis[y], y));
// p.push(make_pair(-dis[to[i]], to[i])); //若为大根堆,可以利用相反数变小根堆
}
}
}
}
Bellman-Ford算法
核心代码
for(k = 1; k <= n - 1; k++)
for(i = 1; i <= m; i++)
if(dis[v[i]] > dis[u[i]] + w[i])
dis[v[i]] = dis[u[i]] + w[i];
SPFA算法
也叫队列优化的bellman-ford算法,主要是可以解决负权图的问题,一个结点可能入队、出队多次,目的是收敛到全部满足三角形不等式状态;队列优化是避免了bellman-Ford算法中对不需要扩展的结点的冗余扫描
queue<int>q;
void add(int u, int v, int w)
{
to[++bcnt] = v;
val[bcnt] = w;
nex[bcnt] = head[u];
head[u] = bcnt;
}
void spfa()
{
memset(dis, 0x3f3f3f3f, sizeof(dis));
memset(vis, 0, sizeof(vis));
dis[1] = 0;
q.push(1);
vis[1] = 1;
while (q.size())
{
int x = q.front();
q.pop();
vis[x] = 0; //弹出来,相应地取消标记
for (int i = head[x]; i; i = nex[i])
{
int y = to[i], z = val[i];
if (dis[y] > dis[x] + z)
{
dis[y] = dis[x] + z;
if (!vis[i])
{
q.push(y);
vis[y] = 1; //推进去,相应的增加标记
}
}
}
}
}
牛客一个小栗子Telephone Lines
二分+双端队列spfaspfa与dijkstra的区别是spfa是不断走,不断试;dijkstra只贪心地走一次
这里处理数据有个小技巧,目的是求出1~n的最短路,双端队列适合处理边权只有0或1的最短路
#include <bits/stdc++.h>
using namespace std;
#pragma GCC optimize(3, "Ofast", "inline")
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<long long, long long> pll;
const int N = 1e4 + 100;
const int inf = 0x3f3f3f3f;
const int mod = 1e9;
#define IOS \
ios::sync_with_stdio(0); \
cin.tie(0); \
cout.tie(0);
ll n, p, k, tot, ans;
ll head[N], nex[N << 1], to[N << 1], val[N << 1], dis[N];//数据小了卡到76
bool vis[N];
void add(ll u, ll v, ll w)
{
to[++tot] = v;
val[tot] = w;
nex[tot] = head[u]; //cao小错也能犯giao
head[u] = tot;
}
bool check(ll x)
{
deque<ll> d; //双端队列bfs
memset(dis, inf, sizeof(dis)); //注意其他点到1的距离初始为
memset(vis, 0, sizeof(vis));
dis[1] = 0;
d.push_back(1);
while (d.size())
{
int t = d.front();
d.pop_front();
if (vis[t]) //
continue;
vis[t] = 1;
for (int i = head[t]; i; i = nex[i])
{
ll y = to[i], z = (val[i] > x ? 1 : 0);
dis[y] = min(dis[y], dis[t] + z); //这里是t,不能习惯性的写x
if (z)//优先扩展最短的,所以当z=0时放队头扩展,z=1时放队尾扩展
d.push_back(y); //一次的转移并没有使得统计的边的条数变大,那么把它放在对头下一个还是由它去增广
else
d.push_front(y); //统计的值变大了,那么就放在队尾,这个值会在比他小的值作为起点都增广完毕之后轮到它增广
}
}
return dis[n] <= k;
}
int main()
{
// IOS;
scanf("%lld%lld%lld", &n, &p, &k);
for (int i = 1; i <= p; i++)
{
ll a, b, l;
scanf("%lld%lld%lld", &a, &b, &l);
add(a, b, l);
add(b, a, l);
}
ll l = 0, r = inf, mid; //l初始为0
while (l < r)
{
mid = (l + r) >> 1; //注意这种配对的二分不是mid=(l+r+1)>>1;
if (check(mid))
r = mid;
else
l = mid + 1;
}
if (l >= inf)
ans = -1;
else
ans = l;
printf("%lld\n", ans);
return 0;
}
牛客一个小栗子Roads and Planes
#include <bits/stdc++.h>
using namespace std;
#pragma GCC optimize(3, "Ofast", "inline")
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<long long, long long> pll;
const int N = 2e5 + 1000;//开大一点
const int inf = 0x3f3f3f3f;
const int mod = 1e9;
#define IOS \
ios::sync_with_stdio(0); \
cin.tie(0); \
cout.tie(0);
ll r, s, t, p, tot, ans, cnt;
ll head[N], nex[N], to[N], val[N], dis[N], deg[N], bel[N];
vector<ll> v[N];
bool vis[N];
void add(ll u, ll v, ll w)
{
to[++tot] = v;
val[tot] = w;
nex[tot] = head[u];
head[u] = tot;
}
void dfs(ll x) //查找连通块
{
bel[x] = cnt;
v[cnt].push_back(x); //加入自己的连通块队伍
for (int i = head[x]; i; i = nex[i]) //访问与自己相关的结点
{
int y = to[i];
if (!bel[y])
dfs(y);
}
}
void dijkstra()
{
queue<ll> q;
priority_queue<pll, vector<pll>, greater<pll>> p;
memset(dis, inf, sizeof(dis));
q.push(s); //
dis[s] = 0;
for (int i = 1; i <= cnt; i++) //
{
if (!deg[i])
q.push(i);
}
while (q.size())
{
int x = q.front();
q.pop();
for (int i = 0; i < v[x].size(); i++) //访问自己的连通块,加入最小堆中
p.push({dis[v[x][i]], v[x][i]});
while (p.size()) //求每个连通块的最短路
{
ll x = p.top().second;
p.pop();
if (vis[x])
continue;
vis[x] = 1;
for (int i = head[x]; i; i = nex[i])
{
ll y = to[i], z = val[i];
if (dis[y] > dis[x] + z)
{
dis[y] = dis[x] + z;
}
if (bel[x] == bel[y]) //仍在连通块内
p.push({dis[y], y});
else
{
deg[bel[y]]--; //所属连通块
if (!deg[bel[y]]) //拓扑排序的操作
q.push(bel[y]);
}
}
}
}
}
int main()
{
// IOS;
scanf("%lld%lld%lld%lld", &t, &r, &p, &s);
for (int i = 1; i <= r; i++)
{
ll x, y, z;
scanf("%lld%lld%lld", &x, &y, &z);
add(x, y, z);
add(y, x, z);
}
for (int i = 1; i <= t; i++)
{
if (!bel[i]) //
cnt++, dfs(i);
}
for (int i = 1; i <= p; i++)
{
ll x, y, z;
scanf("%lld%lld%lld", &x, &y, &z);
add(x, y, z);
deg[bel[y]]++; //入度++
}
dijkstra();
for (int i = 1; i <= t; i++) //题目让求的是到每个城镇最便宜的方案
{
if (dis[i] >= inf)
puts("NO PATH");
else
printf("%lld\n", dis[i]);
}
return 0;
}
任意两点最短路径
Floyed算法
实质是动态规划的思想,dp[k,i,j]表示若干个编号不超过k的结点从i到j的最短路长度,k是阶段,i、j是状态,故k在最外面
状态转移方程为dp[k,i,j]=min(dp[k-1][i][j],dp[k-1][i][k]+dp[k-1][k][j])
同01背包一样,第一维k可以省略
int floyed()
{//将k看作中转站
for (int k = 1; k <= n; k++)
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k][j]);
}
传递闭包
使用floyed算法可以解决传递闭包的问题【dp[i][i]=1,1表示有关系,0表示没关系】
int floyed()
{//将k看作中转站
for (int k = 1; k <= n; k++)
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
dp[i][j]|=dp[i][k]&dp[k][j];
}
牛客一个小栗子Sorting It All Out
拓扑排序做法
#include <bits/stdc++.h>
using namespace std;
#pragma GCC optimize(3, "Ofast", "inline")
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<long long, long long> pll;
const int N = 2e5 + 1000; //开大一点
const int inf = 0x3f3f3f3f;
const int mod = 1e9;
#define IOS \
ios::sync_with_stdio(0); \
cin.tie(0); \
cout.tie(0);
ll n, m, tot, cnt;
ll deg[N], d[N];
char ans[N];
vector<ll> v[N];
ll topu()
{
for (int i = 0; i < n; i++)
d[i] = deg[i]; //
queue<ll> q;
for (int i = 0; i < n; i++)
if (!d[i])
q.push(i);
ll flag = 0, cnt = 0; //
while (q.size())
{
if (q.size() > 1) //不能装反,nmd
flag = 1;
ll x = q.front();
q.pop();
ans[cnt++] = x;
for (int i = 0; i < v[x].size(); i++)
{
if (--d[v[x][i]] == 0) //
q.push(v[x][i]);
}
}
if (cnt != n)
return -1; //
else if (flag)
return 0;
else
return 1;
}
int main()
{
// IOS;
while (scanf("%lld%lld", &n, &m) && n && m)
{
memset(deg, 0, sizeof(deg)); //
for (int i = 0; i < n; i++)
v[i].clear();
char s[5];
ll tmp, f = 0, id; //
for (int i = 1; i <= m; i++)
{
scanf("%s", s);
ll aa = s[0] - 'A', bb = s[2] - 'A';
v[aa].push_back(bb);
deg[bb]++;
if (f)
continue;
tmp = topu();
if (tmp == 1 || tmp == -1) //
f = 1, id = i;
}
if (tmp == 1)
{
printf("Sorted sequence determined after %lld relations: ", id);
for (int i = 0; i < n; i++) //
printf("%c", ans[i] + 'A');
puts(".");
}
else if (tmp == -1)
printf("Inconsistency found after %lld relations.\n", id);
else if (tmp == 0)
printf("Sorted sequence cannot be determined.\n");
}
return 0;
}
Floyd传递闭包做法
#include <bits/stdc++.h>
using namespace std;
#pragma GCC optimize(3, "Ofast", "inline")
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<long long, long long> pll;
const int N = 100;
const int inf = 0x3f3f3f3f;
const int mod = 1e9;
#define IOS \
ios::sync_with_stdio(0); \
cin.tie(0); \
cout.tie(0);
ll n, m;
ll dp[N][N], deg[N];
ll check()
{
ll res = 1;
for (int i = 1; i <= n; i++)
for (int j = i + 1; j <= n; j++)
{
if (!dp[i][j] && !dp[j][i]) //自环
res = -1;
else if (dp[i][j] && dp[j][i]) //无序
return 0; //不是res=0
}
return res;
}
int main()
{
// IOS;
while (scanf("%lld%lld", &n, &m) && n && m)
{
memset(dp, 0, sizeof(dp)); //
for (int i = 1; i <= n; i++) //
dp[i][i] = 1;
char s[5];
bool f = 0; //
for (int id = 1; id <= m; id++)
{
scanf("%s", s);
if (f)
continue;
ll a = s[0] - 'A' + 1, b = s[2] - 'A' + 1;
dp[a][b] = 1;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
dp[i][j] |= dp[i][a] & dp[b][j];
ll tmp = check();
if (tmp == 1)
{
printf("Sorted sequence determined after %lld relations: ", id);
memset(deg, 0, sizeof(deg));
for (int i = 1; i < n; i++) //
for (int j = i + 1; j <= n; j++)
{
if (dp[i][j])
deg[i]++;
else
deg[j]++;
}
for (int i = n - 1; i >= 0; i--)
for (int j = 1; j <= n; j++)
if (deg[j] == i)//==不是=
{
// printf("%c", j + 'A' - 1);
putchar('A' - 1 + j);
break; //
}
puts(".");
f = 1;
continue;
}
else if (tmp == 0)
{
printf("Inconsistency found after %lld relations.\n", id);
f = 1;
continue;
}
}
if (!f)
{
puts("Sorted sequence cannot be determined.");
continue;
}
}
return 0;
}
无向图的最小环问题
牛客一道小栗子Sightseeing trip
#include <bits/stdc++.h>
using namespace std;
#pragma GCC optimize(3, "Ofast", "inline")
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<long long, long long> pll;
const int N = 111;
const int inf = 0x3f3f3f3f;
const int mod = 1e9;
#define IOS \
ios::sync_with_stdio(0); \
cin.tie(0); \
cout.tie(0);
int n, m, tot, res = inf;
int path[N], pos[N][N], e[N][N], dp[N][N];
void init()
{
memset(e, 0x3f, sizeof(e));
for (int i = 1; i <= n; i++)
e[i][i] = 0;
}
void getpath(ll a, ll b) //求从a到b的最短路经过的中间点
{
if (pos[a][b] == 0)
return;
ll tmp = pos[a][b];
getpath(a, tmp); //
path[tot++] = tmp;
getpath(tmp, b); //
}
void floyd()
{
memcpy(dp, e, sizeof e); //
for (int k = 1; k <= n; k++)
{
for (int i = 1; i < k; i++)
for (int j = i + 1; j < k; j++) //i跟j不能重合
if ((ll)e[i][k] + e[k][j] + dp[j][i] < res)
{
res = e[i][k] + e[k][j] + dp[j][i];
tot = 0;
path[tot++] = k; //从k到i
path[tot++] = i;
getpath(i, j); //从i到j
path[tot++] = j;
}
for (int i = 1; i <= n; i++) //计算k-1号点前任意两点的最短路
for (int j = 1; j <= n; j++)
if (dp[i][j] > dp[i][k] + dp[k][j])
{
dp[i][j] = dp[i][k] + dp[k][j];
pos[i][j] = k;
}
}
}
int main()
{
// IOS;
// scanf("%ld%ld", &n, &m);
cin >> n >> m;
init();
for (int i = 1; i <= m; i++)
{
int u, v, w;
// scanf("%ld%ld%ld", &u, &v, &w);
cin >> u >> v >> w;
e[u][v] = e[v][u] = min(e[u][v], w);
}
floyd();
if (res == inf) //
puts("No solution.");
else
for (int i = 0; i < tot; i++)
printf("%lld ", path[i]);
return 0;
}