图论刷题计划与题解1(最短路问题)

文章目录

  • 图论刷题计划与题解1(最短路问题)
    • 题目1:P1629 邮递员送信(建反图做两次dijkstra)
    • 题目2:P1144 最短路计数
    • 题目3: P1828 [USACO3.2]香甜的黄油 Sweet Butter
    • 题目4: P1576 最小花费
    • 题目5: P5767 [NOI1997] 最优乘车
    • 题目6: P5764 [CQOI2005]新年好

图论刷题计划与题解1(最短路问题)

题目1:P1629 邮递员送信(建反图做两次dijkstra)

可以先做一遍dijskra,然后对每个点做一遍回来的dijkstra,代码很复杂,而且显然超时,如果考虑建反图只需要做两次dijkstra,因为每个点到起点的距离等价于起点到各个点的距离,而且在反图上,原来的正向边变反向边,反向边变正向边,两次dijkstra都只可能走正向边。(因为dijkstra本来就是走正向边)
需要注意建反图后,n的范围是2倍,所以需要把n的范围开大,不然会re
P1629

#include
using namespace std;
typedef long long ll;
const int N = 2e3+10,M = 2e5+10;
int h[N],w[M],e[M],ne[M],idx,dist[N];
int n,m; 
bool st[N];
typedef pair<int, int> PII;
void add(int a, int b, int c)  // 添加一条边a->b,边权为c
{
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}
void dijkstra(int s)
{
    dist[s] = 0;
    priority_queue<PII,vector<PII>,greater<PII>>heap;
    heap.push({0,s});
    while(heap.size())
    {
        auto t = heap.top();
        heap.pop();
        int distance = t.first,id = t.second;
        if(st[id]) continue;
        st[id] = true;
        for(int i = h[id];i!=-1;i= ne[i])
        {
            int j = e[i];
            if(dist[j]>distance+w[i])
            {
                dist[j] = distance+w[i];
                heap.push({dist[j],j});
            }
        }
    }
}
int main()
{
    memset(dist,0x3f3f3f3f,sizeof dist);
    memset(h, -1, sizeof h);
    memset(st, 0, sizeof st);
    cin>>n>>m;
    while(m--)
    {
        int a,b,c;
        cin>>a>>b>>c;
        add(a, b, c);
        add(b+n,a+n,c);//小trik +n建反图,但是要
        //注意N的范围
    }
    dijkstra(1);
    ll sum = 0;
    for(int i = 1;i<=n;i++)
    {
        sum+=dist[i];
    }
    memset(st, 0, sizeof st);
    memset(dist,0x3f3f3f3f,sizeof dist);
    dijkstra(1+n);
    for(int i = 2+n;i<=n*2;i++)
    {
        sum+=dist[i];
    }
    cout<<sum;
}

题目2:P1144 最短路计数

这个是对于无向无权图的最短路计数,所以可以直接使用bfs遍历即可,看每个点到1的最短距离是否等于它的前个节点的最短距离+1,如果是则计数。最后输出即可。
P1144

#include 
#include 
#include 
using namespace std;
typedef long long ll;
const ll N = 1000000+10,M = 2000000+10;
ll mod = 100003;
ll h[N], e[M], ne[M], idx,n,m;
ll dist[N],f[N],q[N];
bool st[N];
void add(ll a,ll b)
{//加边
    e[idx] = b;ne[idx] = h[a];h[a] = idx++;
}
void bfs(ll u)
{
    ll hh = 0,tt = -1;
    q[++tt] = u;
    st[u] = true;
    dist[u] = 0;
    f[u] = 1;
    while(hh<=tt)
    {
        auto t = q[hh++];
        for(ll i = h[t];~i;i = ne[i])
        {
            ll j = e[i];
            if(!st[j])
            {
                st[j] = true;
                dist[j] = dist[t]+1;
                q[++tt] = j;
            }
            if(dist[j]==dist[t]+1)
            {
                f[j] = (f[j]+f[t])%mod;
            }
        }
    }
}
int main()
{
    memset(dist,0x3f3f3f3f,sizeof dist);
    memset(st, 0, sizeof st);
    memset(h, -1, sizeof h);
    memset(f,0,sizeof f);
    cin>>n>>m;
    while (m -- ){
        ll a,b;
        cin>>a>>b;
        add(a,b);
        add(b,a);
    }
    bfs(1);
    ll ans = 0;
    for(ll i = 1;i<=n;i++)
    {
        cout<<f[i]%mod<<endl;
        // ans = (ans+f[i])%mod;
    }
    // cout<
    return 0;
}

题目3: P1828 [USACO3.2]香甜的黄油 Sweet Butter

P1828
以每个牧场为起点,都做一遍dijskra,然后定义全局变量ans,每次把ans与奶牛距离之和取min即可
AC code:

#include 
#include 
#include 
#include 
using namespace std;
#define INF 0x3f3f3f3f
typedef pair<int, int> PII;
int n,p,c;
const int N = 1010,M = 2910;
int h[N], e[M], w[M], ne[M], idx;
int dist[N];
bool st[N];
int id[N];
int ans = INF;
void add(int a,int b,int c)
{
    e[idx] = b,w[idx] = c,ne[idx] = h[a],h[a] = idx++;
}
int dijskra(int u)
{
    for(int i = 1;i<=p;i++)
    {    
        dist[i] = INF;
        st[i] = false;
    }
    dist[u] = 0;
    priority_queue<PII,vector<PII>,greater<PII>>heap;
    heap.push({0,u});//编号为1,距离为0
    while(heap.size())
    {
        auto t = heap.top();
        heap.pop();
        int distance = t.first,id = t.second;
        if(st[id]) continue;
        st[id] = true;
        for(int i = h[id];~i;i = ne[i])
        {
            int j = e[i];
            if(dist[j]>distance+w[i])
            {
                dist[j] = distance+w[i];
                heap.push({dist[j],j});
            }
        }
    }        
    int sum = 0;
    for(int j = 1;j<=n;j++)
    {
        if(dist[id[j]]==INF) return INF;
        sum+=dist[id[j]];
    }
    return sum;
}
int main()
{
    /*对每个牧场做一次dijkstra,用ans记录最短的总距离*/
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    memset(h, -1, sizeof h);
    cin>>n>>p>>c;
    for(int i = 1;i<=n;i++)
        cin>>id[i];//每只奶牛所在的牧场号
    while(c--)
    {
        int a,b,w;
        cin>>a>>b>>w;
        add(a,b,w);
        add(b,a,w);
    }
    for(int i = 1;i<=p;i++)
    {
        ans = min(ans,dijskra(i));
    }
    cout<<ans;
}

题目4: P1576 最小花费

P1576
dijskra的变种,需要注意如何建图,找到乘积最大的边,然后100/dist[n]即可。

#include 
#include 
#include 
#include 

using namespace std;

typedef pair<double,int>PDI;
const int N = 4010,M = 2e5+10;
int h[N],e[M],ne[M],idx;
bool st[N];
double dist[N];
double w[M];
int n,m;
void add(int a,int b,double c)
{
    e[idx] = b,w[idx] = (1-c*0.01),ne[idx] = h[a],h[a] = idx++;
}
void dijiskra(int start)
{
    dist[start] = 1;
    priority_queue<PDI,vector<PDI>,less<PDI>>heap;
    heap.push({1,start});
    while(heap.size())
    {
        auto t = heap.top();
        heap.pop();
        int id = t.second;
        double distance = t.first;
        if(st[id]) continue;
        st[id] = true;
        for(int i = h[id];~i;i = ne[i])
        {
            int j = e[i];
            if(dist[j]<distance*w[i])
            {
                dist[j] = distance*w[i];
                if(!st[j])
                {
                    heap.push({dist[j],j});
                }
            }
        }
    }
}
int main()
{
    // ios::sync_with_stdio(false);
    // cin.tie(0);
    // cout.tie(0);
    //反向做一遍dijskra
    memset(h, -1, sizeof h);
    memset(dist,-0x3f3f3f3f,sizeof dist);
    cin>>n>>m;
    while (m -- ){
        int a,b,c;
        cin>>a>>b>>c;
        add(a,b,c);
        add(b,a,c);
    }
    int start,end;
    cin>>end>>start;
    dijiskra(start);
    printf("%.8lf",100/dist[end]);
}

题目5: P5767 [NOI1997] 最优乘车

P5767
需要注意是有向图,且建图方式比较特别,学习stringstream的使用,另外要找到一个性质:换乘次数即为最少坐车次数-1

//本来以为是无向图,但是仔细阅读题意后,发现是有向图
//观察到数据范围小,所以可以邻接矩阵建图
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
const int N = 510,M = 110;
int a[N],q[N];
int g[N][N];
int n,m;
int dist[N];
void bfs(int u)
{
    memset(dist,0x3f3f3f3f,sizeof dist);
    int hh = 0,tt = -1;
    q[++tt] = u;
    dist[1] = 0;
    while(hh<=tt)
    {
        auto t = q[hh++];
        for(int i = 1;i<=n;i++)
        {
            if(g[t][i]&&dist[i]>dist[t]+1)
            {
                dist[i] = dist[t]+1;
                q[++tt] = i;
            }
        }
    }
}
int main()
{
    memset(g,0,sizeof dist);
    cin>>m>>n;
    string line;
    getline(cin,line);
    while (m--){
        //建图
        getline(cin,line);
        stringstream ssin(line);
        int cnt = 0,p;
        while(ssin>>p) a[cnt++] = p;
        for(int j = 0;j<cnt;j++)
        {
            for(int k = j+1;k<cnt;k++)
            {
                g[a[j]][a[k]] = 1;
            }
        }
    }
    bfs(1);
    if(dist[n]==0x3f3f3f3f)
    {
        cout<<"NO";
    }
    else
    {
        cout<<dist[n]-1;
    }
    return 0;
}

题目6: P5764 [CQOI2005]新年好

P5764
先做dijskra预处理dist数组,注意这里需要开二维的dist数组,然后dfs枚举所有情况即可

#include
using namespace std;
const int N = 5e4+10,M = 2e5+10;
typedef pair<int, int> PII;
int h[N], e[M], w[M], ne[M], idx;
int dist[6][N],n,m;
bool st[N];
bool vis[N];//爆搜时用来标记状态
int a[N],ans;
#define INF 1e9
void add(int a,int b,int c)
{
    e[idx] = b,w[idx] = c,ne[idx] = h[a],h[a] = idx++;
}
void dijkstra(int u,int dist[])
{
    memset(dist,0x3f3f3f3f,4*N);//只memset一维'
    memset(st, 0, sizeof st);
    dist[u] = 0;
    priority_queue<PII,vector<PII>,greater<PII>>heap;
    heap.push({0,u});
    while(heap.size())
    {
        auto t = heap.top();
        heap.pop();
        int id = t.second,distance = t.first;
        if(st[id]) continue;
        st[id] = true;
        for(int i = h[id];~i;i = ne[i])
        {
            int j = e[i];
            if(dist[j]>distance+w[i])
            {
                dist[j] = distance+w[i];
                heap.push({dist[j],j});
            }
        }
    }
}

int dfs(int u,int start,int distance)
{
    if(u==6)
    {
        return distance;
    }
    int res = INF;
    for(int i = 1;i<=5;i++)
    {
        if(!vis[i])
        {
            int next = a[i];
            vis[i] = true;
            res = min(res,dfs(u+1,i,distance+dist[start][next]));
            vis[i] = false;
        }
    }
    return res;
}
int main()
{
    memset(h, -1, sizeof h);
    cin>>n>>m;
    a[0] = 1;
    for(int i = 1;i<=5;i++)
    {
        cin>>a[i];
    }
    while (m -- ){
        int a,b,c;
        cin>>a>>b>>c;
        add(a, b, c);
        add(b, a, c);
    }
    for(int i = 0;i<6;i++)
        dijkstra(a[i],dist[i]);//先做dijskra预处理dist数组
    cout<<dfs(1,0,0);
}

你可能感兴趣的:(#,最短路与分层图,图论,算法,深度优先)