最近就是图论专题啦 因为图论的题目真的超级多 然后可能没办法做到日更了因为开始上课了
文章目录
最短路问题
P5905 【模板】Johnson 全源最短路\
题目链接
就是一道板子题
这个算法这里讲得挺详细的可以去看看
OI-Wiki
但是这道题卡了spfa!
不 它没卡也卡不了
我判负环条件写错了(t[i] == n + 1)
AC代码:
#include <cstring>
#include <iostream>
#include <queue>
#define INF 1e9
using namespace std;
struct edge
{
int v,w,next;
}e[10005];
struct node
{
int dis,id;
bool operator<(const node&a)const
{
return dis>a.dis;
}
node(int d,int x)
{
dis=d,id=x;
}
};
int head[5005],vis[5005],t[5005];
int cnt,n,m;
long long h[5005],dis[5005];
void addedge(int u,int v,int w)
{
e[++cnt].v=v;
e[cnt].w=w;
e[cnt].next=head[u];
head[u]=cnt;
}
bool spfa(int s)
{
queue<int> q;
memset(h,63,sizeof(h));
h[s]=0,vis[s]=1;
q.push(s);
while(!q.empty())
{
int u=q.front();
q.pop();
vis[u]=0;
for(int i=head[u];i;i=e[i].next)
{
int v=e[i].v;
if(h[v]>h[u]+e[i].w)
{
h[v]=h[u]+e[i].w;
if(!vis[v])
{
vis[v]=1;
q.push(v);
t[v]++;
if(t[v]==n + 1)return false;
}
}
}
}
return true;
}
void dijkstra(int s)
{
priority_queue<node> q;
for(int i=1;i<=n;i++)
dis[i]=INF;
memset(vis,0,sizeof(vis));
dis[s]=0;
q.push(node(0,s));
while(!q.empty())
{
int u=q.top().id;
q.pop();
if(vis[u])continue;
vis[u]=1;
for(int i=head[u];i;i=e[i].next)
{
int v=e[i].v;
if(dis[v]>dis[u]+e[i].w)
{
dis[v]=dis[u]+e[i].w;
if(!vis[v])q.push(node(dis[v],v));
}
}
}
return;
}
int main()
{
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int u,v,w;
cin>>u>>v>>w;
addedge(u,v,w);
}
for(int i=1;i<=n;i++)
addedge(0,i,0);
if(!spfa(0))
{
cout<<-1<<endl;
return 0;
}
/*
for(int i=1;i<=n;i++)
cout<<h[i]<<' ';
cout<<endl;
*/
for(int u=1;u<=n;u++)
for(int i=head[u];i;i=e[i].next)
e[i].w+=h[u]-h[e[i].v];
for(int i=1;i<=n;i++)
{
dijkstra(i);
long long ans=0;
for(int j=1;j<=n;j++)
{
if(dis[j]==INF)ans+=j*INF;
else ans+=j*(dis[j]+h[j]-h[i]);
}
cout<<ans<<endl;
}
return 0;
}
P1144 最短路计数
题目链接
题意如题目
那就直接多维护一个计数数组就行啦
AC代码:
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
const int N = 1e6 + 10;
const int mod = 1e5 + 3;
int n,m,d[N];
struct edge
{
int v,w;
};
vector<edge> g[N];
struct node
{
int d,u;
bool operator < (const node& b) const{
return d > b.d;
}
};
int ans[N];
bool vis[N];
void dijkstra(int s)
{
priority_queue<node> q;
memset(d,0x3f,sizeof(d));
d[s] = 0;
q.push((node){0,s});
while(!q.empty()){
node x = q.top();q.pop();
int u = x.u;
if(vis[u]) continue;
vis[u] = 1;
for(int i = 0;i < g[u].size();i ++){
int v = g[u][i].v;
int w = g[u][i].w;
if(d[v] > d[u] + w){
d[v] = d[u] + w;
ans[v] = ans[u];
q.push((node){d[v],v});
}
else if(d[v] == d[u] + w){
ans[v] += ans[u];
ans[v] %= mod;
}
}
}
}
int main()
{
cin >> n >> m;
for(int i = 0,u,v;i < m;i ++){
cin >> u >> v;
g[u].push_back((edge){v,1});
g[v].push_back((edge){u,1});
}
ans[1] = 1;
dijkstra(1);
for(int i = 1;i <= n;i ++){
cout << ans[i] << endl;
}
return 0;
}
P1462 通往奥格瑞玛的道路
题目链接
看到交费最多的最小值
我们直接二分答案
用这个答案去跑dijkstra就行
AC代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
#define MAXN 1e9 + 10
using namespace std;
const int N = 1e4 + 10;
int n,m,hp;
int d[N],f[N];
bool vis[N];
struct edge
{
int v,w;
};
vector<edge> g[N];
struct node
{
int d,u;
bool operator < (const node& b) const {
return d > b.d;
}
};
bool check(int x)
{
if(x < f[1]) return false;
for(int i = 1;i <= n;i ++)d[i] = 1e9;
memset(vis,0,sizeof(vis));
d[1] = 0;
priority_queue<node> q;
q.push((node){0,1});
while(!q.empty()){
node s = q.top();q.pop();
int u = s.u;
if(vis[u])continue;
vis[u] = 1;
for(int i = 0;i < g[u].size();i ++){
int v = g[u][i].v;
int w = g[u][i].w;
if(f[v] <= x && !vis[v] && d[v] > d[u] + w){
d[v] = d[u] + w;
q.push((node){d[v],v});
}
}
}
if(d[n] < hp) return true;
else return false;
}
int main()
{
cin >> n >> m >> hp;
for(int i = 1;i <= n;i ++)cin >> f[i];
for(int i = 0;i < m;i ++){
int u,v,w;cin >> u >> v >> w;
g[u].push_back((edge){v,w});
g[v].push_back((edge){u,w});
}
if(!check(MAXN)){
cout << "AFK" << endl;
return 0;
}
int l = 1,r = MAXN;//二分费用
while(l <= r){
int mid = l + r >> 1;
if(check(mid)) r = mid - 1;
else l = mid + 1;
}
cout << l << endl;
return 0;
}
P1522 [USACO2.4]牛的旅行 Cow Tours
题目链接
好的看到N <= 150 我们直接暴力
注意后来的牧场可能比原来的还小!!
AC代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define db double
using namespace std;
const int N = 200,INF = 1e9;
char G[N][N];
int n;db f[N][N];
struct node{
db x,y;
}a[N];
db d[N];
int fa[N];
int getf(int x){return x == fa[x] ? x : fa[x] = getf(fa[x]);}
db gd(int i,int j){
return sqrt((a[i].x - a[j].x) * (a[i].x - a[j].x) + (a[i].y - a[j].y) * (a[i].y - a[j].y));
}
int main(){
scanf("%d",&n);
for(int i = 1;i <= n;i ++){
for(int j = 1;j <= n;j ++){
if(i == j) f[i][j] = 0;
else f[i][j] = INF;
}
}
for(int i = 1;i <= n;i ++) fa[i] = i;
for(int i = 1;i <= n;i ++)scanf("%lf%lf",&a[i].x, &a[i].y);
for(int i = 1;i <= n;i ++){
for(int j = 1;j <= n;j ++){
scanf("\n%c",&G[i][j]);
if(G[i][j] == '1'){
f[i][j] = gd(i,j);
int fi = getf(i);
int fj = getf(j);
if(fi != fj) fa[fi] = fj;
}
}
}
for(int k = 1;k <= n;k ++){
for(int i = 1;i <= n;i ++){
for(int j = 1;j <= n;j ++){
f[i][j] = min(f[i][j],f[i][k] + f[k][j]);
}
}
}
db now = 0;
for(int i = 1;i <= n;i ++){
for(int j = 1;j <= n;j ++){
if(f[i][j] != INF && i != j){
d[i] = max(f[j][i],d[i]);
}
}
now = max(d[i],now);
}
db ans = 1e9;
for(int i = 1;i <= n;i ++){
for(int j = i + 1;j <= n;j ++){
int fi = getf(i);
int fj = getf(j);
if(f[i][j] == INF && fi != fj){
ans = min(ans,gd(i,j) + d[i] + d[j]);
}
}
}
ans = max(now,ans);
printf("%.6f",ans);
return 0;
}
P1266 速度限制
题目链接
由于N只有150,K只有500
所以我们可以开一个二维数组维护最短路具体实现见代码
AC代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define db double
using namespace std;
const int N = 155,M = 510;
const db INF = 1e9;
int n,m,s;
struct edge{
int next,to,l,v;
}e[30000];
int head[30000],cnt;
void add(int u,int v,int l,int vv){
e[++ cnt].next = head[u];
e[cnt].l = l;
e[cnt].v = vv;
e[cnt].to = v;
head[u] = cnt;
}
struct node{
db d;int s,v;
node(db d,int s,int v): d(d),s(s),v(v) {}
bool operator < (const node& b)const{
return d > b.d;
}
};
db d[N][M];
int pre[N][M][2];
bool vis[N][M];
void dijkstra(int s){
for(int i = 1;i <= n;i ++){
for(int j = 0;j <= 500;j ++){
d[i][j] = INF;
}
}
priority_queue<node> q;
d[s][70] = 0;
q.push(node(0,s,70));
while(!q.empty()){
int u = q.top().s,v = q.top().v;
q.pop();
if(vis[u][v]) continue;
vis[u][v] = 1;
for(int i = head[u];i;i = e[i].next){
int to = e[i].to,vv = e[i].v;
int l = e[i].l;
if(!vv){
if(d[to][v] > d[u][v] + 1.0 * l / v){
d[to][v] = d[u][v] + 1.0 * l / v;
pre[to][v][0] = u;
pre[to][v][1] = v;
q.push(node(d[to][v],to,v));
}
}
else{
if(d[to][vv] > d[u][v] + 1.0 * l / vv){
d[to][vv] = d[u][v] + 1.0 * l / vv;
pre[to][vv][0] = u;
pre[to][vv][1] = v;
q.push(node(d[to][vv],to,vv));
}
}
}
}
}
void solve(int now,int v){
if(now - 1) solve(pre[now][v][0],pre[now][v][1]);
printf("%d ",now - 1);
}
int main(){
scanf("%d%d%d",&n,&m,&s);
for(int i = 1;i <= m;i ++){
int u,v,l,vv;
scanf("%d%d%d%d",&u,&v,&vv,&l);
add(u + 1,v + 1,l,vv);
}
dijkstra(1);
int v = 0;
for(int i = 1;i <= 500;i ++){
if(d[s + 1][i] < d[s + 1][v]) v = i;
}
solve(s + 1,v);
return 0;
}
P4568 [JLOI2011]飞行路线
题目链接
分层图 好题!
分层图的意思就是建立k个一摸一样的图(k是免费飞行次数)
然后上层往下层连边,边权设为0
然后求s到t + k * n的最短路就ok
AC代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N = 1e5 + 1e4;
struct edge{
int next,to,w;
}e[2500010];
int cnt,head[N];
void add(int u,int v,int w){
e[++ cnt].next = head[u];
head[u] = cnt;
e[cnt].to = v;
e[cnt].w = w;
}
int d[N];
bool vis[N];
struct node{
int d,s;
node(int d,int s):d(d),s(s){}
bool operator < (const node& b)const{
return d > b.d;
}
};
void dijkstra(int s){
memset(d,0x3f,sizeof(d));
priority_queue<node> q;
d[s] = 0;
q.push(node(0,s));
while(!q.empty()){
int u = q.top().s;q.pop();
if(vis[u]) continue;
vis[u] = true;
for(int i = head[u];i;i = e[i].next){
int v = e[i].to;
if(d[v] > d[u] + e[i].w){
d[v] = d[u] + e[i].w;
q.push(node(d[v],v));
}
}
}
}
int n,m,k,s,t;
int main(){
scanf("%d%d%d%d%d",&n,&m,&k,&s,&t);
for(int i = 0;i < m;i ++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
add(v,u,w);
for(int j = 1;j <= k;j ++){
add(u + (j - 1) * n,v + j * n,0);
add(v + (j - 1) * n,u + j * n,0);
add(v + j * n,u + j * n,w);
add(u + j * n,v + j * n,w);
}
}
for(int i = 1;i <= k;i ++){
add(t + (i - 1) * n,t + i * n,0);
}
dijkstra(s);
printf("%d",d[t + k * n]);
return 0 ;
}
P5304 [GXOI/GZOI2019]旅行者
题目链接
开O2优化压线过的
好像还有一种跑正反dijsktra染色但是我不会
以后再看看吧
AC代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10,M = 7e5 + 10;
struct edge{
int next,to,w;
}e[M],te[M];
int head[N],back[N],cnt;
void add(int u,int v,int w){
e[++ cnt].next = head[u];
e[cnt].to = v;
e[cnt].w = w;
head[u] = cnt;
}
long long d[N];
struct node{
long long d;
int s;
node(long long d,int s):d(d),s(s){}
bool operator < (const node& a)const{
return d > a.d;
}
};
int s,t;
long long dijkstra(int s){
memset(d,0x3f,sizeof(d));
d[s] = 0;
priority_queue<node> q;
q.push(node(0,s));
while(!q.empty()){
int u = q.top().s;q.pop();
for(int i = head[u];i;i = e[i].next){
int v = e[i].to;
if(d[v] > d[u] + e[i].w){
d[v] = d[u] + e[i].w;
q.push(node(d[v],v));
}
}
}
return d[t];
}
int n,m,k;int node[N];
int main(){
int T;
scanf("%d",&T);
while(T --){
memset(head,0,sizeof(head));
cnt = 0;
scanf("%d%d%d",&n,&m,&k);
for(int i = 0;i < m;i ++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
}
for(int i = 1;i <= k;i ++) scanf("%d",&node[i]);
long long ans = 1e10;
s = n + 1,t = n + 2;
for(int i = 0;(1 << i) <= k;i ++){
memcpy(back,head,sizeof(head[0]) * (n + 3));
int tot = cnt;
for(int j = 1;j <= k;j ++){
if(j & (1 << i)) add(s,node[j],0);
else add(node[j],t,0);
}
ans = min(ans,dijkstra(s));
cnt = tot;
memcpy(head,back,sizeof(head[0]) * (n + 3));
for(int j = 1;j <= k;j ++){
if(j & (1 << i)) add(node[j],t,0);
else add(s,node[j],0);
}
ans = min(ans,dijkstra(s));
cnt = tot;
memcpy(head,back,sizeof(head[0]) * (n + 3));
}
printf("%lld\n",ans);
}
return 0;
}