洛谷试炼日记(图论)(解题报告)

最近就是图论专题啦 因为图论的题目真的超级多 然后可能没办法做到日更了因为开始上课了

最短路问题

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;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值