【NOIP复习】最短路总结

【模板】

 1 /*堆优化Dijkstra*/
 2
 3 void dijkstra()
 4 {
 5     priority_queue<pair<ll,int>,vector<pair<ll,int> >,greater<pair<ll,int> > > que;//定义大顶堆
 6     for (int i=1;i<=n;i++) vis[i]=0,dis[i]=INF;
 7     dis[1]=0;
 8     que.push(make_pair<ll,int>(0,1));
 9     while (!que.empty())
10     {
11         int head=que.top().second;que.pop();
12         vis[head]=1;//从堆中弹出时表明找到了最短路,不再访问
13         for (int i=0;i<E[head].size();i++)
14         {
15             edge now=E[head][i];
16             if (!vis[now.to] && dis[now.to]>dis[head]+(ll)now.len)
17             {
18                 dis[now.to]=dis[head]+(ll)now.len;
19                 que.push(make_pair<ll,int>(dis[now.to],now.to));
20             }
21         }
22     }
23     printf("%lld",dis[n]);
24 }
 1 /*SPFA*/
 2 void spfa(int start)
 3 {
 4     memset(inque,0,sizeof(inque));
 5     for (int i=1;i<=tot;i++) dis[i]=INF;
 6     dis[start]=0;
 7     inque[start]=1;
 8     que.push(start);
 9     while (!que.empty())
10     {
11         int head=que.front();que.pop();
12         inque[head]=0;
13         for (int i=0;i<E[head].size();i++)
14         {
15             int to=E[head][i].to,len=E[head][i].len;
16             if (dis[to]>dis[head]+len)
17             {
18                 dis[to]=dis[head]+len;
19                 if (!inque[to])
20                 {
21                     inque[to]=1;
22                     que.push(to);
23                 }
24             }
25         }
26     }
27 }
 1 /*dfs版本SPFA-判断是否存在负环*/
 2 void spfa(int u)
 3 {
 4     if (vis[u])
 5     {
 6         flag=1;
 7         return;
 8     }
 9     vis[u]=1;
10     for (int i=0;i<E[u].size();i++)
11     {
12         int to=E[u][i].to,len=E[u][i].len;
13         if (dis[to]>dis[u]+len)
14         {
15             dis[to]=dis[u]+len;
16             spfa(to);
17             if (flag) return;
18         }
19     }
20     vis[u]=0;
21 }
22
23 void solve()
24 {
25     memset(vis,0,sizeof(vis));
26     flag=0;
27     for (int i=1;i<=n;i++)
28     {
29         spfa(i);
30         if (flag) break;
31     }
32     puts(flag?"YES":"NO");
33 }
1 /*Floyd*/
2     for (int i=0;i<n;i++)
3         for (int j=0;j<n;j++)
4             for (int k=0;k<n;k++)
5                 f[i][j]=min(f[i][j],f[i][k]+f[k][j]);

例题

【POJ1062】-有限制条件的最短路

题意:每个人都有一个物品,对应一定的钱数,想要得到此物品可以直接出钱,也可以通过用其他人的物品并添加一些钱来交换优惠购买物品。另外,每个人都有一个等级,要求和你交易的所有人中不能有任何两人的等级相差m以上。为了方便起见,我们把所有的物品从1开始进行编号,酋长的允诺也看作一个物品,并且编号总是1。每个物品都有对应的价格P,主人的地位等级L,以及一系列的替代品Ti和该替代品所对应的"优惠"Vi。如果两人地位等级差距超过了M,就不能"间接交易"。你必须根据这些数据来计算出探险家最少需要多少金币才能娶到酋长的女儿。

思路:由于物品编号从1开始,我们假定0也是一个物品作为起点,它到其它物品的距离就是各个物品的原始价值。开始时,如果两种物品主人的等级限制M在规定范围以内,且j能用i替换,则将优惠价格视作从i到j的一条权值为优惠价的路径;如果在范围以外,就设为INF。处理主人地位等级限制条件的方法:我们依次枚举每一个物品,将它的等级L作为交易中等级最高的那一个,即可以参与交易的等级范围为[L-M,L],预处理时将这个范围以外的物品强制设置为已经访问过,再进行Dijkstra即可。

【POJ3255&BZOJ1726】-次短路

[方法一]在跑Dijkstra的过程中保留次短路。对于Dijkstra判断中取出的每一个点,如果到它的最短距离大于当前该点的次短距离,则当前该点已经取到最短距离和次短距离,不进行操作,否则进行两次判断:如果小于最短边,则赋给最短变,并将最短边赋给次短边;或者如果大于最短变且小于次短边,则赋给次短边。两次完成之后均要加入优先队列(!)。

 1 /*核心代码*/
 2         for 从当前点抵达的每一个点
 3         {
 4             int d=head.len+w[k];
 5             if (dis[v[k]]>d)
 6             {
 7                 swap(dis[v[k]],d);
 8                 temp.len=dis[v[k]];temp.num=v[k];
 9                 que.push(temp);
10             }
11             if (dis[v[k]]<d && secondis[v[k]]>d)
12             {
13                 secondis[v[k]]=d;
14                 temp.len=secondis[v[k]];temp.num=v[k];
15                 que.push(temp);
16             }
17         }

[方法二]正反跑两次SPFA。枚举每一条边,如果起点到一个端点的最短路+另一个端点到终点的最短路+该边长度 ≠ 最短路,则和答案比较更新最小值。

1 /*核心代码*/
2 /*dis1为起点到各个点的最短路,dis2为各个点到终点的最短路(又终点开始方向跑的最短路)*/
3    for (int i=1;i<=r;i++)
4     {
5         int now=dis1[u[i]]+dis2[v[i]]+w[i];
6         if (now!=mx) ans=min(ans,now);
7         now=dis1[v[i]]+dis2[u[i]]+w[i];
8         if (now!=mx) ans=min(ans,now);
9     }

【POJ1511】-从源点出发返回源点的最短路

思路:先跑一遍SPFA求出单源点最短路,再方向建图重新跑一次最短路。两者相加得到的和即为答案。

【POJ1860】-判断正环&带操作的边长

题意:有若干种货币,某些币种之间可兑换,给出各种兑换时的汇率和手续费,任何兑换都是双向的,但是两个方向的汇率和手续费可能不同,并告知你现在拥有的货币种类(只拥有一种)及数量,问是否可以通过货币建兑换最后回到本币种后钱数有所增加。

思路:SPFA跑最长路。注意题目中有向图的边长是需要通过运算得到的,按照一般最短路判断写法就可以了。

if (did[v]<(dis[u]-手续费)*汇率)

(不知道链接里一年前的自己在讲些什么)

BZOJ3436】-差分约束

题意:有n个值a1..an,得到一些诸如:ai与aj之差不大于x,ai与aj之差不小于y的约束条件,ai与aj相等的约束条件。问是否有可能?

思路:a>=b+c → b<=a-c,由a向b连一条-c的边;a<=b+c,由b向a连一条c的边;a=b → b<=a<=b,互相连一条0的边。dfs版SPFA判断负环。

POJ3169】-差分约束

n头牛从小到大排,它们之间某些距离不能大于一个值,某些距离不能小于一个值,求第一头牛和第N头牛之间距离的最大值。

思路:如BZOJ3436构造边,区别为本题限制条件不存在环状,最后求的是dis[n]-dis[i]<=S的S值,戟dis[n]<=dis[i]+S,用最短路径即可。

另外,如果题目要 求的是最大值,就将约束条件转化为">="形式,跑最长路。

【BZOJ4152】-建图!

题意:给定平面上的n个点,定义(x1,y1)到(x2,y2)的费用为min(|x1-x2|,|y1-y2|),求从1号点走到n号点的最小费用。

思路:按照某维坐标排序,相邻两个点在这一维度上的差值最小,所以两两连边,长度为这一维度上的差值然后跑最短路即可。

OpenJudge3368】-限制访问顺序,多条路径覆盖全图的最小值(!)

题意:诸葛亮要征服N城市。然而,City-X在击败City-2,City-3……City-x-1后击败。关羽,张飞,赵云,每个人都应该领导一个军队。三个军队从City-0出发,征服所有的城市并返回City-0。求三个军队的行程总长的最小值。

思路:首先求出最短路径。我们用dp[i][j][k]表示当前三个部队分别位于i、j、k时的答案(j<=k<=i)。分别考虑从i、j、k中的一支部队走到i+1的情况,总共三个递推式。对于返回,直接假设他们都走到最终状态后统一返回,即0到各自最终状态的最短路径。由于dp[i+1]只和dp[i]有关,可以使用滚动数组。

 1 /*核心代码*/
 2 for(int i=0;i<n;++i)
 3     {
 4         cur^=1;
 5         memset(dp[cur],0x3f,sizeof(dp[cur]));//注意每次滚动数组都要清成无穷大,i=3的时候dp[0][0][0]!=0
 6         for(int j=0;j<=i;++j)
 7             for(int k=j;k<=i;++k)
 8             {
 9                 dp[cur][j][k]=min(dp[cur][j][k],dp[1-cur][j][k]+dist[i+1][i]);
10                 dp[cur][k][i]=min(dp[cur][k][i],dp[1-cur][j][k]+dist[j][i+1]);
11                 dp[cur][j][i]=min(dp[cur][j][i],dp[1-cur][j][k]+dist[k][i+1]);
12                 if (i==n-1)
13                 {
14                     ans=min(ans,(ll)dp[cur][j][k]+(ll)dist[0][n]+(ll)dist[0][j]+(ll)dist[0][k]);
15                     ans=min(ans,(ll)dp[cur][k][i]+(ll)dist[0][n]+(ll)dist[0][k]+(ll)dist[0][i]);
16                     ans=min(ans,(ll)dp[cur][j][i]+(ll)dist[0][n]+(ll)dist[0][j]+(ll)dist[0][i]);
17                 }
18             }
19     }

BZOJ4093】含有中枢的最短路(!!)

题意:有连接N (1 < = N < = 20,000)个农场的航班。对于任何航班,指定了其中的k个农场作为枢纽。 (1 < = K <= 200 , K < = N)。目前,共有M种单向航班( 1 < = M < = 20,000 ),第i个航班从农场u_i至农场v_i花费d_i ( 1 < = d_i < =10,000 )美元。航班保证u_i或者v_i至少有一个是枢纽,任意两个农场至多只有一个航班,保证u_i≠v_i。共收到Q个度假请求,(1 < = Q < = 50,000),其中第i个请求是从农场a_i至农场b_i,问每个请求是否满足 ,并计算能满足的度假请求的最小费用总和。

思路:显然,每个非中枢的周围必定是中枢,也就是说,中枢间可以通过直接相连,或通过一个非中枢点连接。预处理 直接相连 或 间隔一个非中枢点 相连的两个中枢的距离,用Floyd求出所有中枢之间的最短路。接下来处理出所有的中枢到所有节点之间的最短路。由于已知两个中枢(u,v)之间的最短路,对于从v出发抵达的下一个农场vv,很容易得到(u,vv)。最后对于查询的(a,b),如果a是一个中枢,直接可以利用上一步处理出来的信息。否则枚举a指向的每一个中枢c和b之间的最短路,再加上ac的距离,最小的那个即为最短路。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXK=200+5;
const int MAXN=20000+50;
const ll INF=1e12;
struct node
{
    int to,dis;
};
vector<node> E[MAXN];
int n,m,k,q;
ll d[MAXK][MAXK],dis[MAXK][MAXN];
int id[MAXN],num[MAXN];

void addedge(int u,int v,int w)
{
    E[u].push_back((node){v,w});
}

void init()
{
    scanf("%d%d%d%d",&n,&m,&k,&q);
    memset(id,0,sizeof(id));

    for (int i=1;i<=m;i++)
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        addedge(u,v,w);
    }
    for (int i=1;i<=k;i++)
    {
        int x;
        scanf("%d",&x);
        id[x]=i,num[i]=x;
    }
}

void prep()
{
    for (int i=1;i<=k;i++)
        for (int j=1;j<=k;j++)
            d[i][j]=INF;
    for (int i=1;i<=k;i++) d[i][i]=0;
    for (int i=1;i<=k;i++)
    {
        int u=num[i];
        for (int j=E[u].size()-1;j>=0;j--)
        {
            int v=E[u][j].to;
            if (id[v]) d[i][id[v]]=min(d[i][id[v]],(ll)E[u][j].dis);
                else
                {
                    for (int _k=E[v].size()-1;_k>=0;_k--)
                    {
                        int vto=E[v][_k].to;
                        if (id[vto] && vto!=num[i]) d[id[u]][id[vto]]=min(d[id[u]][id[vto]],(ll)E[u][j].dis+(ll)E[v][_k].dis);
                    }
                }
        }
    }    

    for (int _k=1;_k<=k;_k++)
        for (int i=1;i<=k;i++)
            for (int j=1;j<=k;j++)
                if (i!=j && j!=_k && _k!=i) d[i][j]=min(d[i][j],d[i][_k]+d[_k][j]);
}

void prep2()
{
    for (int i=1;i<=k;i++)
        for (int j=1;j<=n;j++) dis[i][j]=INF;
    for (int i=1;i<=k;i++)
        for (int j=1;j<=k;j++) dis[i][num[j]]=d[i][j];

    for (int i=1;i<=k;i++)
    {
        for (int _k=0;_k<E[num[i]].size();_k++)//注意这里是E[num[i]]不是num[i],检查了40分钟才发现QAQ
            for (int j=1;j<=k;j++)
            {
                int to=E[num[i]][_k].to;
                dis[j][to]=min(dis[j][to],d[j][i]+E[num[i]][_k].dis);
            }
    }
}

void solve()
{
    int t=0;
    ll totalans=0;
    for (int i=0;i<q;i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        ll ans=INF;
        if (id[a]) ans=dis[id[a]][b];
            else
            {
                for (int j=0;j<E[a].size();j++)
                {
                    int v=E[a][j].to;
                    if (id[v]) ans=min(ans,E[a][j].dis+dis[id[v]][b]);
                }
            }
        if (ans<INF)
        {
            totalans+=ans;
            t++;
        }
    }
    printf("%d\n",t);
    printf("%d",totalans);
}

int main()
{
    init();
    prep();
    prep2();
    solve();
    return 0;
}

完整代码

BZOJ1706】-恰巧经过n条边的最短路。

思路:Floyd矩阵乘法快速幂

 1 /*核心代码*/
 2 node operator * (node a,node b)
 3 {
 4     node c;
 5     for (int i=1;i<=cnt;i++)
 6         for (int j=1;j<=cnt;j++) c.dis[i][j]=INF;
 7     for (int k=1;k<=cnt;k++)
 8         for (int i=1;i<=cnt;i++)
 9             for (int j=1;j<=cnt;j++)
10                 c.dis[i][j]=min(c.dis[i][j],a.dis[i][k]+b.dis[k][j]);
11     return c;
12 }
13
14 void solve()
15 {
16     int k=n-1;
17     result=f,origin=f;//f为初始的邻接矩阵
18     while (k)
19     {
20         if (k&1) result=result*origin;
21         k>>=1;
22         origin=origin*origin;
23     }
24     printf("%lld",result.dis[id[s]][id[e]]);
25 }


估计NOIP不会考的平面图转最小割

【BZOJ1001狼抓兔子】?

【BZOJ2007海拔】?

时间: 2024-10-03 21:41:12

【NOIP复习】最短路总结的相关文章

【不定期更新】noip复习(或许有误请指正)

一.基本算法 2.二分查找 void find(int l,int r){ if (l>r || a[l]>x || a[r]<x) return; int mid = (l+r) >> 1; if (a[mid] == x){ if (mid < pos)pos=mid; if (a[mid-1] == x) find(l, mid-1); if (a[mid+1] == x) find(mid+1, r); return; } if (x > a[mid])

[算法第一轮复习] 最短路算法之dijkstra

1.算法描述 dijkstra,一种求单源正权图上的最短路的算法 主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止 Dijkstra算法思想为:设G=(V,E)是一个带权有向图,把图中顶点集合V分成两组,第一组为已求出最短路径的顶点集合(用S表示,初始时S中只有一个源点,以后每求得一条最短路径 , 就将 加入到集合S中,直到全部顶点都加入到S中,算法就结束了),第二组为其余未确定最短路径的顶点集合(用U表示),按最短路径长度的递增次序依次把第二组的顶点加入S中.在加入的过程中,总保持从源

蒟蒻林荫小复习——K短路的A*解法

看标题都知道讲的是什么,但为什么特指是A*关于K短路的解法呢? 因为林荫不会其他的. 能看到这篇博客的估计也都知道K短路和A*分别是什么了吧,下面只介绍一下估价函数 由于林荫并没有经过学术训练,所以一下关于A*的理解均为感性,仅可作为OIer速成知识点时的一点资料, 切莫作为算法学术依据. 先思考一下,对于任意一条K短路,是不是均可写成由一部分最短路和一部分其他路径组成,而且这两部分路径还有且仅有一个公共点. 说明:1.任何一条路径都可以视为由其他到终点的路径和终点到终点的最短路组成 2.哪怕前

复习最短路 spfa+dijstra堆优化

题目很简单,, 但是wa了三次,, 用<vector>之前一定要记得clear()...简单说下 spfa的问题 和bell_forman有点类似 每次取出一个点 然后更新 并把更新了的节点入队(更新的值可能会影响到最优解) 当队列为空的时候算法结束(无法优化)这里的vis数组是为了防止重复入队 但每个节点可能多次入队 所以在拿出来的时候 vis标记要消去最后说下负环的问题 引用一下 对于不存在负权回路的图来说,上述算法是一定会结束的.因为算法在反复优化各个最短路径长度,总有一个时刻会进入&q

noip复习模板

我只会这么多 tarjan:codevs 1332 void tarjan(int u) { dfn[u]=low[u]=Time++; s.push(u); for(int i=head[u];~i;i=nxt[i]) { int v=to[i]; if(!dfn[v]) tarjan(v); else if(dfn[v]!=-1) low[u]=Min(low[u],low[v]); } if(dfn[u]==dfn[v]) { while(!s.empty()) { int x=s.top

[NOIP复习]第二章:动态规划

一.背包问题 1.Wikioi 1014 装箱问题 题目描述 Description 有一个箱子容量为V(正整数,0<=V<=20000),同时有n个物品(0<n<=30),每个物品有一个体积(正整数). 要求n个物品中,任取若干个装入箱内,使箱子的剩余空间为最小. 输入描述 Input Description 一个整数v,表示箱子容量 一个整数n,表示有n个物品 接下来n个整数,分别表示这n 个物品的各自体积 输出描述 Output Description 一个整数,表示箱子剩余

noip复习——线性筛(欧拉筛)

整数的唯一分解定理: \(\forall A\in \mathbb {N} ,\,A>1\quad \exists \prod _{i=1}^{s}p_{i}^{a_{i}}=A\),其中\({\displaystyle p_{1}<p_{2}<p_{3}<\cdots <p_{s}}\)而且 \(p_{i}\)是一个质数, \(a_{i}\in \mathbb {Z} ^{+}\)(摘自维基百科) 欧拉筛通过使每个整数只会被它的最小质因子筛到来保证时间复杂度,可以用来筛质数

noip复习之数学(3)——数论

1.基本概念和常用代码 (1)素数(质数) int prime[maxn],tot=0; bool vis[maxn]; void init(int n) { vis[1]=1; for(int i=2;i<=n;++i) { if(!vis[i]) prime[++tot]=i; for(int j=1;j<=tot&&prime[j]*i<=n;++j) { vis[i*prime[j]]=1; if(i%prime[j]==0) break;//每个数都只会被它最小

noip复习之数学(2)——递推关系

1.问题一:兔子的繁殖(斐波拉契数列) ? f[n]=f[n-1]+f[n-2] \[ Fibonacci:f(n)=\frac{1}{\sqrt{5}}\left(\frac{1+\sqrt{5}}{2}\right)^n-\frac{1}{\sqrt{5}}\left(\frac{1-\sqrt{5}}{2}\right)^n \] 问题二:凸三角形剖分/括号化问题/不同出栈次序数问题/二叉树个数问题(卡特兰数) f[n]=f[2]f[n-1]+f[3]f[n-2]+...+f[n-1]f[