kuangbin最短路专题

青蛙POJ2253

传送门
在二维平面上有 n个石头。
第一个石头上有一个青蛙,第二个石头上也有一个青蛙。
第一个石头上的青蛙要到第二个石头上找另一个青蛙玩耍。
它可以直接跳到第二个石头上,也可以经过一系列中间石头,然后再跳到第二个石头上。
这只青蛙希望,它的跳跃过程中,单次跳跃的最长距离尽可能短。
请你计算并输出这个最长距离的最小可能值。
输入格式
输入包含多组测试数据。
每组数据第一行包含一个整数 n。
接下来 n行,第 i 行包含两个整数 xi,yi,表示第 i个石头的位置坐标。
每组数据输入完毕后,还会输入一个空行。
当输入 n=0时,表示输入结束。
输出格式
每组数据先输出一行 Scenario #x,其中 x是组别编号(从 1开始)。然后输出一行 Frog Distance = y,其中 y是单次跳跃的最长距离的最小可能值,保留三位小数。
每组数据输出完毕后,还要输出一行空行(最后一组数据也不例外)。
数据范围
2≤n≤200,0≤xi,yi≤1000。
输入样例:

2
0 0
3 4
3
17 4
19 4
18 5
0

输出样例:

Scenario #1
Frog Distance = 5.000

Scenario #2
Frog Distance = 1.414

解析
参考了 MangataTS 巨巨的题解,才把题目写出来。QAQ
最大路径最小值问题
当前正在对起点t,终点j做一个松弛操作,那么做的不是dist[j]=max(dist[j],dist[t]+w)而是dist[j]=max(dist[t],w)我们这样就能记录下最长边了,那么现在的dist[i]表示的就是从1开始到i结束的最长边的长度,而不是累计长度

int n;
db dist[M];

db dis( PDD a , PDD b ){
    return sqrt( pow( a.first-b.first , 2 ) + pow( a.second-b.second , 2 ) );
}

void solve(){
    vector<PDD>xoy(n);
    rep( i , 0 , n+1 ) dist[i] = INF;

    rep( i , 0 , n ) cin >> xoy[i].first >> xoy[i].second;

    dist[0] = 0.0;
    priority_queue<PDI,vector<PDI>,greater<PDI>>q;
    q.push( { 0 , 0 } );

    while( q.size() ){
        auto t = q.top();
        q.pop();

        db len = t.first;
        int st = t.second;

        rep( i , 0 , n ){
            db temp = max( len , dis( xoy[st] , xoy[i] ) );
            if( temp < dist[i] ){
                dist[i] = temp;
                q.push( { dist[i] , i } );
            }
        }
    }
}

int main(){ 
    int i = 1;
    while( cin >> n && n ){
        solve();
        cout << "Scenario #" << i++ << endl;
        printf("Frog Distance = %.3lf\n\n",dist[1]);
    }
    return 0;
}

货物运输POJ1797

传送门

给定一个 n 个点 m条边的无重边无自环的无向图。点的编号为 1∼n。
现在,要从点 1到点 n运货。
已知每条边的最大承重,请你计算一次最多可以运多少货物。
输入格式
第一行包含整数 T,表示共有 T组测试数据。
每组数据第一行包含两个整数 n,m。
接下来 m行,每行三个整数 a,b,c,表示点 a 和点 b 之间有一条无向边,最大承重为 c。
输出格式
每组数据先输出一行 Scenario #x,其中 x是组别编号(从 1开始)。然后一行,输出一个整数,表示一次性最多可运货物数量。
最后输出一个空行。
数据范围
1≤T≤10,1≤n≤1000,
1≤m≤n(n−1)2,
1≤a,b≤n,
1≤c≤106。
保证一定有解。
输入样例:

1
3 3
1 2 3
1 3 4
2 3 5

输出样例:

Scenario #1:
4

题解
最小路径最大,与上一道题目的解法相反

int n,m;
int mp[M][M],dist[M];

void solve(){
    cin >> n >> m;
    memset( mp , 0 , sizeof mp );
    memset( dist , 0 , sizeof dist );
    while( m-- ){
        int u,v,w;
        cin >> u >> v >> w;
        mp[u][v] = w;
        mp[v][u] = w;
    }

    dist[0] = INF;
    priority_queue<PII>q;
    q.push( { INF , 1 } );

    while( q.size() ){
        auto t = q.top();
        q.pop();

        int st = t.second;
        int l = t.first;

        if( l < dist[st] ) continue;

        rep( i , 1 , n+1 ){
            if( temp > dist[i] ){
                dist[i] = temp;
                q.push( { dist[i] , i } );
            }
        }
    }

}

int main(){
    int t;
    scanf("%d",&t);
    rep( i ,1 , t+1 ){
        solve();
        printf("Scenario #%d:\n",i);
        printf("%d\n\n",dist[n]);
    }
    return 0;
}

农场派对USACO

传送门
N 头牛要去参加在某农场举行的一场编号为 X的牛的派对。有 M条有向道路,每条路长 Ti;每头牛参加完派对后都必须回到家,每头牛都会选择最短路径。
求这 N头牛的最短路径(一个来回)中最长的一条的长度。
特别提醒:可能有权值不同的重边。
输入格式
第一行包含三个整数 N,M,X。
接下来 M行,每行包含三个整数 Ai,Bi,Ti,表示有一条从 Ai 到 Bi 的路,长度为 Ti。
输出格式
共一行,一个数,表示最短路中最长的一条的长度。
数据范围
1≤N≤1000,
1≤X≤N,
1≤M≤105,
1≤Ti≤100
输入样例:

4 8 2
1 2 4
1 3 2
1 4 7
2 1 1
2 3 5
3 1 2
3 4 4
4 2 3

输出样例:

10

题解
刚刚开始时题意理解错了,做了一次假题。
题意就是让我们求出最短路。
有个小技巧,就是从终点开始跑最短路模板还一个就是反向见建图。

int n,m,x;
bool st[M];
int mp[M][M],dist[M];
int remp[M][M],redist[M];

void dijkstra( int be ){
//正向建图跑dj的含义是从终点返回的流程
    dist[ be ] = 0;
    rep( i , 0 , n ){
        int t = -1;
        for( int j = 1 ; j <= n ; j++ )
            if( !st[j] && ( t == -1 || dist[j] < dist[t] ) )
                t = j;
        rep( j , 1 , n+1 ){
            dist[j] = min( dist[j] , dist[t]+mp[t][j] );
        }
        st[t] = 1;
    }
}

void redijkstra( int be ){
//反向建图跑dj的含义是各个起点到终点的过程
//不过在代码中写法还是从终点开始往各个点拓展的过程
    redist[ be ] = 0;
    rep( i , 0 , n ){
        int t = -1;
        for( int j = 1 ; j <= n ; j++ )
            if( !st[j] && ( t == -1 || redist[j] < redist[t] ) )
                t = j;
        rep( j , 1 , n+1 ){
            redist[j] = min( redist[j] , redist[t]+remp[t][j] );
        }
        st[t] = 1;
    }
}

int main(){ 
    memset( redist , 0x3f , sizeof redist );
    memset( remp , 0x3f , sizeof remp );
    memset( dist , 0x3f , sizeof dist );
    memset( mp , 0x3f , sizeof mp );
    cin >> n >> m >> x;
    while( m-- ){
        int u,v,w;
        cin >> u >> v >> w;
        mp[u][v] = min( mp[u][v] ,w );
        remp[v][u] = min( remp[v][u] , w );
    }
    dijkstra( x );
    memset( st , 0 , sizeof st );
    redijkstra( x );
    int ans = 0;
    rep( i , 1 , n+1 ) ans = max( ans , dist[i]+redist[i] );
    cout << ans;
    return 0;
}

货币兑换POJ1860

某国家流通有 N 种货币,编号 1∼N。
该国家设有 M个货币兑换点,每个兑换点提供两种货币之间的兑换业务。
每个兑换点都自行设定货币兑换汇率以及服务费。
每个兑换点的基本信息可以用 6个数字 A,B,RAB,CAB,RBA,CBA 来描述,表示该兑换点提供第 A 种货币和第 B 种货币之间的相互兑换,并且从 A 兑换到 B 的兑换汇率为 RAB,服务费为 CAB,从 B 兑换到 A 的兑换汇率为 RBA,服务费为 CBA。
A到 B 的兑换汇率就是 1 单位 A 货币可以换得的 B货币的数量。服务费必须在兑换之前,先行从来源货币处扣除。
例如,如果从 A兑换 B 的兑换汇率为 29.75,服务费为 0.39,则用 100 元 A 货币可以兑换 (100−0.39)∗29.75=2963.3975 元 B货币。
一个商人目前手上有 V元 S 货币,他想要通过先将自己手中的钱进行辗转兑换,最终再次换回 S货币的方式,让自己手中的钱变得更多。
请你判断他能否做到。
输入格式
第一行包含四个数字 N,M,S,V。
接下来 M行,每行包含 6 个数字 A,B,RAB,CAB,RBA,CBA。
输出格式
如果可以做到让钱变多,则输出 YES,否则输出 NO。
数据范围
1≤S≤N≤100,
1≤M≤100,
0≤V≤1000,
汇率取值范围 [10−2,102],
服务费取值范围 [0,100]。
输入样例:

3 2 1 20.0
1 2 1.00 1.00 1.00 1.00
2 3 1.10 1.00 1.10 1.00
输出样例:
YES

题解

判断是否存在从正权回路

int n, m,s;
double w[N],fee[N],v;
int h[N], e[N], ne[N], idx;
double dist[N];
bool st[N];//存储当前队列中的点,避免重复推入。

void add(int a, int b, double c , double d)
{
    e[idx] = b, w[idx] = c , fee[idx] = d , ne[idx] = h[a], h[a] = idx ++ ;
}

int spfa()
{
    memset(dist, 0x3f, sizeof dist);
    dist[s] = v;

    queue<int> q;
    q.push(s);
    st[s] = true;

    while (q.size())
    {
        int t = q.front();
        q.pop();

        st[t] = false;

        for (int i = h[t]; i != -1; i = ne[i])
        {
            int j = e[i];
            if (dist[j] < (dist[t]-fee[i])*w[i])
            {
                dist[j] = (dist[t]-fee[i])*w[i];
                if( j == s ) return 1;
                if (!st[j])
                {
                    q.push(j);
                    st[j] = true;
                }
            }
        }
    }

    return 0;
}

int main()
{
    scanf("%d%d%d%lf", &n, &m,&s,&v);
    memset(h, -1, sizeof h);
    while (m -- ){
        int a, b;
        double rab , cab , rba , cba;
        scanf("%d%d%lf%lf%lf%lf", &a, &b, &rab, &cab, &rba, &cba);
        add(a, b, rab , cab );
        add( b , a , rba , cba );
    }
    int t = spfa();
    if ( !t ) puts("NO");
    else puts("YES");
    return 0;
}

虫洞POJ3259

传送门
农夫约翰在巡视他的众多农场时,发现了很多令人惊叹的虫洞。
虫洞非常奇特,它可以看作是一条 单向 路径,通过它可以使你回到过去的某个时刻(相对于你进入虫洞之前)。
农夫约翰的每个农场中包含 N片田地,M 条路径(双向)以及 W个虫洞。
现在农夫约翰希望能够从农场中的某片田地出发,经过一些路径和虫洞回到过去,并在他的出发时刻之前赶到他的出发地。他希望能够看到出发之前的自己。

请你判断一下约翰能否做到这一点。

下面我们将给你提供约翰拥有的农场数量 F,以及每个农场的完整信息。
已知走过任何一条路径所花费的时间都不超过 10000
秒,任何虫洞将他带回的时间都不会超过 10000秒。
输入格式
第一行包含整数 F,表示约翰共有 F个农场。
对于每个农场,第一行包含三个整数 N,M,W。
接下来 M行,每行包含三个整数 S,E,T,表示田地 S 和 E 之间存在一条路径,经过这条路径所花的时间为 T。
再接下来 W行,每行包含三个整数 S,E,T,表示存在一条从田地 S 走到田地 E 的虫洞,走过这条虫洞,可以回到 T秒之间。
输出格式
输出共 F行,每行输出一个结果。
如果约翰能够在出发时刻之前回到出发地,则输出 YES,否则输出 NO。

数据范围
1≤F≤5
1≤N≤500,
1≤M≤2500,
1≤W≤200,
1≤T≤10000,
1≤S,E≤N

输入样例:

2
3 3 1
1 2 2
1 3 4
2 3 1
3 1 3
3 2 1
1 2 3
2 3 4
3 1 8

输出样例:

NO
YES

spfa判断负环的模板题目

int n,m1,m2,idx,cnt[N];
int dist[N];bool st[N];
int h[N],w[N],ne[N],e[N];

void init(){
    idx = 0;
    memset( h , -1 , sizeof h );
    memset( dist , 0x3f , sizeof dist );
    rep( i , 0 , n+1 ) {
        cnt[i] = ne[i] = e[i] = w[i] = st[i] = 0;
    }
}

void add( int u , int v , int val ){
    e[idx] = v , w[idx] = val , ne[idx] = h[u] , h[u] = idx++;
}

bool spfa(){
    queue<int>q;
    rep( i , 1 , n+1 ){
        st[i] = 1;
        q.push( i );
    }
    while( q.size() ){
        int t = q.front();
        q.pop();

        st[t] = 0;

        for( int i = h[t] ; i != -1 ; i = ne[i] ){
            int j = e[i];
            if( dist[j] > dist[t]+w[i] )
            {
                dist[j] = dist[t] + w[i];
                cnt[j] = cnt[t] + 1;
                if( cnt[j] >= n ) return true;
                if( !st[j] )
                {
                    q.push( j );
                    st[j] = 1;
                }
            }
        }
    }

    return false;
}

string solve(){
    init();
    cin >> n >> m1 >> m2;
    rep( i , 1 , m1+1 ){
        int u,v,val;
        cin >> u >> v >> val;
        add( u , v , val );
        add( v , u , val );
    }
    rep( i , 1 , m2+1 ){
        int u,v,val;
        cin >> u >> v >> val;
        add( u , v , -val );
    }
    if( spfa() ) return "YES";
    else return "NO";
}

int main(){
    int _;
    cin >> _;
    while( _-- )
        cout << solve() << endl;
    return 0;
}

传递信息POJ1502

机房有 n 台服务器,编号 1∼n。
其中一些服务器之间可以互相传递信息。
不同服务器之间传递信息的时间可能不同。

请你计算,从 1号服务器发出一个信息,传递到其他所有服务器,所需花费的最短时间。
输入格式
第一行包含整数 n。
第 2∼n行,第 i 行包含 i−1 个正整数或字符 x,用来描述第 i 号服务器和前 i−1 号服务器的连接状况。如果第 j 个值 Aij 是正整数,则表示服务器 i 和服务器 j 之间可以相互传递信息,传递时间为 Aij;如果第 j 个值 Aij 是 x,则表示服务器 i 和服务器 j之间无法相互传递信息。
输出格式
输出一个整数,表示所需花费的最短时间。
数据范围
1≤n≤100,服务器之间的传递时间取值范围 [1,100]。
保证一定有解。
输入样例:

5
50
30 5
100 20 50
10 x x 10

输出样例:

35

int n;
bool st[N];
int mp[M][M],dist[N];

void dijkstra(){
    dist[1] = 0;
    rep( i , 0 , n ){
        int t = -1;
        rep( j , 1 , n+1 )
            if( !st[j] && ( t == -1 || dist[j] < dist[t] ) )
                t = j;
        rep( j , 1 , n+1 )
            dist[j] = min( dist[j] , dist[t]+mp[t][j] );
        st[t] = 1;
    }
}

int main(){
    memset( mp , 0x3f , sizeof mp );
    memset( dist , 0x3f , sizeof dist );
    cin >> n;
    rep( i , 2 , n+1 ){
        rep( j , 1 , i ){
            string s;
            cin >> s;
            if( s == "x" ) continue;
            int w = stoi( s );
            mp[i][j] = min( mp[i][j] , w );
            mp[j][i] = min( mp[j][i] , w );
        }
    }
    dijkstra();
    int ans = 0;
    rep( i , 2 , n+1 )
        if( dist[i] != INF ) ans = max( dist[i] , ans );
    cout << ans;
    return 0;
}

牛的比赛POJ3660

N 头奶牛,编号 1∼N,一起参加比赛。
奶牛的战斗力两两不同。
这些奶牛之间已经进行了 M轮两两对决。
在对决中,战斗力高的奶牛一定会战胜战斗力低的奶牛。

请问,通过上述 M轮对决的结果,可以确定多少头奶牛的具体战斗力排名。
输入格式
第一行包含两个整数 N,M。
接下来 M行,每行包含两个整数 a,b,表示奶牛 a 和奶牛 b 之间进行了对决,并且奶牛 a 战胜了奶牛 b。
输出格式
输出可以确定具体战斗力排名的奶牛数量。
数据范围
1≤N≤100,1≤M≤4500,
数据保证合法。
输入样例:

5 5
4 3
4 2
3 2
1 2
2 5

输出样例:

2

样例解释
2号奶牛输给了 1,3,4 号奶牛,战胜了 5 号奶牛,可以确定它的战斗力排名为 4。
5号奶牛输给了排在第 4 的 2 号奶牛,所以它的战斗力排名为 5。
其它奶牛不确定。

传递闭包问题

int n,m;
int mp[M][M];

void floyd(){
    rep( k , 1 , n+1 )
        rep( i , 1 , n+1 )
            rep( j , 1 , n+1 )
                mp[i][j] |= (mp[i][k]&mp[k][j]);
}

int main(){
    cin >> n >> m;
    while( m-- ){
        int u,v;
        cin >> u >> v;
        mp[u][v] = 1;
    }
    floyd();
    int ans = 0;
    rep( i , 1 , n+1 ){
        int cnt = 0;
        rep( j , 1 , n+1 )
            if( mp[i][j] || mp[j][i] ) cnt++;
        if( cnt == n-1 ) ans++;
    }
    cout << ans;
    return 0;
}

最短路径和POJ1511

给定一个 n 个点 m条边的无重边无自环的有向图。
点的编号 1∼n。
任意两点之间均可相互抵达。

请你计算,从点 1到其他所有点的最短路径长度以及从其他所有点到点 1 的最短路径长度,并将这 2×(n−1)个最短路径长度相加后输出结果。
输入格式
第一行包含整数 T,表示共有 T组测试数据。
每组数据第一行包含两个整数 n,m。
接下来 m行,每行包含三个正整数 a,b,c,表示从点 a 到点 b 存在一条边,长度为 c。
输出格式
每组数据输出一行,一个整数表示结果。
数据范围
1≤T≤10,
1≤n,m≤106,
1≤a,b≤n,
一组数据中所有的 c 相加之和不超过 10^9。
输入样例:

2
2 2
1 2 13
2 1 33
4 6
1 2 10
2 1 60
1 3 20
3 4 10
2 4 5
4 1 50

输出样例:

46
210

int n,m,idx,reidx;
bool st[N],rest[N];
int dist[N],redist[N];
int h[N],e[N],ne[N],w[N];
int reh[N],ree[N],rene[N],rew[N];

void add( int a , int b , int c ){
    e[idx] = b , w[idx] = c , ne[idx] = h[a] , h[a] = idx++;
}

void readd( int a , int b , int c ){
    ree[reidx] = b , rew[reidx] = c , rene[reidx] = reh[a] , reh[a] = reidx++;
}

void init(){
    idx = 0, reidx = 0;
    memset( st , 0 , sizeof st );
    memset( rest , 0 , sizeof rest );
    memset( h , -1 , sizeof h );
    memset( reh , -1 , sizeof reh );
}

void dijkstra(){
    memset( dist , 0x3f , sizeof dist );
    dist[1] = 0;
    priority_queue< PII , vector<PII> , greater<PII> > q;
    q.push( {0,1} );
    while( q.size() ){
        auto t = q.top();
        q.pop();
        int ver = t.second;

        if( st[ver] ) continue;
        st[ver] = 1;

        for( int i = h[ver] ; i != -1 ; i = ne[i] )
        {
            int j = e[i];
            if( dist[j] > dist[ver]+w[i] ){
                dist[j] = dist[ver]+w[i];
                q.push( { dist[j] , j } );
            }
        }
    }
}

void redijkstra(){
    memset( redist , 0x3f , sizeof redist );
    redist[1] = 0;
    priority_queue< PII , vector<PII> , greater<PII> > q;
    q.push( {0,1} );
    while( q.size() ){
        auto t = q.top();
        q.pop();
        int ver = t.second;

        if( rest[ver] ) continue;
        rest[ver] = 1;

        for( int i = reh[ver] ; i != -1 ; i = rene[i] )
        {
            int j = ree[i];
            if( redist[j] > redist[ver]+rew[i] ){
                redist[j] = redist[ver]+rew[i];
                q.push( { redist[j] , j } );
            }
        }
    }
}

LL solve(){
    LL ans = 0;
    init();
    cin >> n >> m;
    while( m-- ){
        int a,b,c;
        cin >> a >> b >> c;
        add( a , b , c );
        readd( b , a , c );
    }
    dijkstra();
    redijkstra();
    rep( i , 2 , n+1 ) ans += dist[i];
    rep( i , 2 , n+1 ) ans += redist[i];
    return ans;
}

int main(){
    int t;
    cin >> t;
    while( t-- )
        cout << solve() << endl;
    return 0;
}

糖果POJ3159

有 n 个小朋友,编号 1∼n。
老师要给他们发糖果。
小朋友们的攀比心都很重,现在给出 m条攀比信息。
每条信息包含三个整数 a,b,c,含义是小朋友 a 认为小朋友 b 的糖果数量最多只可以比他多 c个,否则他就生气。
老师在发糖果时,必须照顾所有小朋友的情绪,让他们都感到满意。
请问,小朋友 n最多比小朋友 1多分到多少个糖果。
输入格式
第一行包含两个整数 n,m。
接下来 m行,每行包含三个整数 a,b,c,表示一条攀比信息。
输出格式
一个整数,表示小朋友 n最多比小朋友 1多分到的糖果数量的最大可能值。
数据范围
2≤n≤30000,
1≤m≤150000,
1≤a,b≤n,
1≤c≤10000。
保证一定有解。
输入样例:

2 2
1 2 5
2 1 4

输出样例:

5

int n,m,idx;
int dist[N];bool st[N];
int h[N],e[N],ne[N],w[N];

void add( int u , int v , int val ){
    e[idx] = v , w[idx] = val , ne[idx] = h[u] , h[u] = idx++;
}

int dijsktra(){
    memset( dist , 0x3f , sizeof dist );
    dist[1] = 0;
    priority_queue< PII , vector<PII> , greater<PII> > q;
    q.push( {0,1} );
    while( q.size() ){
        int ver = q.top().second;
        q.pop();

        if( st[ver] ) continue;
        st[ver] = 1;

        for( int i = h[ver] ; i != -1 ; i = ne[i] ){
            int j = e[i];
            if( dist[j] > dist[ver]+w[i] ){
                dist[j] = dist[ver]+w[i];
                q.push( { dist[j] , j } );
            }
        }
    }
    return dist[n]-dist[1];
}

int main(){
    memset( h , -1 , sizeof h );
    scanf("%d%d",&n,&m);
    while( m-- ){
        int u,v,val;
        scanf("%d%d%d",&u,&v,&val);
        add( u , v , val );
    }
    printf("%d\n",dijsktra());
    return 0;
}

地铁POJ2502

你每天早上都要从家赶往学校。
你可以选择步行或者乘坐地铁赶路。
你的步行速度是 10千米/小时,地铁的行驶速度是 40千米/小时。
假设你足够幸运,任何时刻你抵达任何地铁站,都能赶上一个立即发车的地铁。
你可以多次上下地铁,也可以随意换乘不同地铁线路。
所有地铁线路都是双向的。
请你计算,你在路中需要花费的最少时间。
输入格式
第一行输入你的家和学校位置的 x,y坐标。
随后若干行,每行描述一条地铁线路。按线路顺序输出每个线路中包含的所有站点的 x,y坐标。地铁在相邻站点之间沿直线行驶,坐标均表示整数米,每条线路至少包含两个站点,所有站点坐标输入结束后,行末会跟有虚拟坐标 -1 -1。
输出格式
输出你到学校所需的最少分钟数。四舍五入取整。
数据范围
输入所有坐标均为整数。
输入中最多包含 200个站点。
输入样例:

0 0 10000 1000
0 200 5000 200 7000 200 -1 -1
2000 600 5000 600 10000 600 -1 -1

输出样例:

21

点数200较少,直接floyd。难点在于建图的思路

vector<PII> be;
db mp[M][M],ans;

double dist( int u ,int v ){
    return sqrt( pow( be[u].fi-be[v].fi , 2 ) + pow( be[u].se-be[v].se, 2 ) )*60/10000;
}

int main(){
    rep( i , 0 , M )
        rep( j , 0 , M )
            if( i == j ) mp[i][j] = 0;
            else mp[i][j] = INF; 

    rep( i , 0 , 2 ){
        int u,v;
        scanf("%d%d",&u,&v);
        be.pb( {u,v} );
    }

    int a,b,idx=2;
    while( cin >> a >> b )
        if( a == -1 && b == -1 ){
            rep( i , idx , (int)be.size() )
                rep( j , i+1 , (int)be.size() )
                    mp[i][j] = mp[j][i] = mp[i][j-1]+dist( j-1 , j )/4;
            idx = be.size();
        }else be.pb( { a,b } );

    rep( i , 0 , (int)be.size() )
        rep( j , 0 , (int)be.size() )
            mp[i][j] = min( mp[i][j] , dist( i , j ) );

    rep( k , 0 , (int)be.size() )
        rep( i , 0 , (int)be.size() )
            rep( j , 0 , (int)be.size() )
                mp[i][j] = min( mp[i][j] , mp[i][k]+mp[k][j] );

    printf("%d",(int)(mp[0][1]+0.5));
    return 0;
}

昂贵的聘礼POJ1062

年轻的探险家来到了一个印第安部落里。
在那里他和酋长的女儿相爱了,于是便向酋长去求亲。
酋长要他用 10000个金币作为聘礼才答应把女儿嫁给他。

探险家拿不出这么多金币,便请求酋长降低要求。

酋长说:”嗯,如果你能够替我弄到大祭司的皮袄,我可以只要 8000
金币。如果你能够弄来他的水晶球,那么只要 5000金币就行了。”

探险家就跑到大祭司那里,向他要求皮袄或水晶球,大祭司要他用金币来换,或者替他弄来其他的东西,他可以降低价格。

探险家于是又跑到其他地方,其他人也提出了类似的要求,或者直接用金币换,或者找到其他东西就可以降低价格。
不过探险家没必要用多样东西去换一样东西,因为不会得到更低的价格。
探险家现在很需要你的帮忙,让他用最少的金币娶到自己的心上人。
另外他要告诉你的是,在这个部落里,等级观念十分森严。
地位差距超过一定限制的两个人之间不会进行任何形式的直接接触,包括交易。
他是一个外来人,所以可以不受这些限制。
但是如果他和某个地位较低的人进行了交易,地位较高的的人不会再和他交易,他们认为这样等于是间接接触,反过来也一样。

因此你需要在考虑所有的情况以后给他提供一个最好的方案。
为了方便起见,我们把所有的物品从 1开始进行编号,酋长的允诺也看作一个物品,并且编号总是 1。
每个物品都有对应的价格 P,主人的地位等级 L,以及一系列的替代品 Ti 和该替代品所对应的”优惠” Vi。
如果两人地位等级差距超过了 M,就不能”间接交易”。
你必须根据这些数据来计算出探险家最少需要多少金币才能娶到酋长的女儿。
输入格式
输入第一行是两个整数 M,N,依次表示地位等级差距限制和物品的总数。
接下来按照编号从小到大依次给出了 N个物品的描述。
每个物品的描述开头是三个非负整数 P、L、X,依次表示该物品的价格、主人的地位等级和替代品总数。
接下来 X行每行包括两个整数 T 和 V,分别表示替代品的编号和”优惠价格”。
输出格式
输出最少需要的金币数。
数据范围
1≤N≤100,
1≤P≤10000,
1≤L,M≤N,
0≤X<N
输入格式

1 4
10000 3 2
2 8000
3 5000
1000 2 1
4 200
3000 2 1
4 200
50 2 0

输出格式

5250

dj题,建图的过程有点难理解,虽然是第二次写这道题目,依旧感觉两眼一抹黑脑子一片空白QAQ

int n,m;
bool st[M];
int mp[M][M],dist[M],level[M];

int dijkstra( int down , int up ){
    memset( dist , 0x3f , sizeof dist );
    memset( st , 0 , sizeof st );
    dist[0] = 0;
    rep( i , 0 , n ){
        int t = -1;
        rep( j , 0 , n+1 )
            if( !st[j] && ( t == -1 || dist[j] < dist[t] ) )
                t = j;
        rep( j , 1 , n+1 )
            if( level[j] >= down && level[j] <= up )
                dist[j] = min( dist[j] , dist[t]+mp[t][j] );
        st[t] = 1;
    }

    return dist[1];
}

int main(){
    memset( mp , 0x3f , sizeof mp );
    scanf("%d%d",&m,&n);
    rep( i , 1 , n+1 ) mp[i][i] = 0;
    rep( i , 1 , n+1 ){
        int price,cnt;
        scanf("%d%d%d",&price,&level[i],&cnt);
        mp[0][i] = min( mp[0][i] , price );
        while( cnt-- ){
            int id,prices;
            scanf("%d%d",&id,&prices);
            mp[id][i] = min( mp[id][i] , prices );
        }
    }
    int ans = INF;
    rep( i , level[1]-m , level[1]+1 ) ans = min( ans , dijkstra( i , i+m ) );
    printf("%d",ans);
    return 0;
}

电车POJ1847

某城市共有 n 个电车站点,编号 1∼n。

现在,我们要乘坐电车从 A站点前往 B站点。

电车在第 i个站点可以驶往 ki个其他站点,其中一个站点为默认驶往站点,如果电车驶往非默认站点,则需要进行一次变道。

我们希望从 A到 B的过程中,电车变道的次数尽可能少。
请问,最少需要进行多少次变道。
输入格式
第一行包含三个整数 n,A,B。
接下来 n行,其中第 i 行首先包含一个整数 ki,表示第 i 个站点可以驶往的站点数量,然后包含 ki 个整数,表示每个可驶往的站点编号,其中第 1个给出的站点为默认驶往站点。
输出格式
一个整数,表示最少变道次数。
如果无法从 A
到 B,则输出 −1。
数据范围
2≤N≤100,
1≤A,B≤N,
0≤Ki≤N−1。
输入样例:

3 2 1
2 2 3
2 3 1
2 1 2

输出样例:

0

dj板子题目

int n,a,b;
int mp[M][M];
bool st[M];int dist[M];

int dijkstra(){
    memset( dist , 0x3f , sizeof dist );
    dist[a] = 0;
    rep( i , 0 , n ){
        int t = -1;
        rep( j , 1 , n+1 )
            if( !st[j] && ( t == -1 || dist[t] > dist[j] ) )
                t = j;
        rep( j , 1 , n+1 )
            dist[j] = min( dist[j] , dist[t]+mp[t][j] );
        st[t] = 1;
    }
    if( dist[b] == INF ) return -1;
    else return dist[b];
}

int main(){
    memset( mp , 0x3f , sizeof mp );
    scanf("%d%d%d",&n,&a,&b);
    rep( i , 1 , n+1 ){
        int t;scanf("%d",&t);
        rep( j , 1 , t+1 ){
            int u; scanf("%d",&u);
            if( j == 1 ) mp[i][u] = 0;
            else mp[i][u] = 1;
        }
    }
    printf("%d",dijkstra());
    return 0;
}

Nya图最短路HDU4725

Nya 图是一种分层的无向图。

图中共有 N个节点,每个节点都处于某一层当中。
层与层之间存在阶梯。利用阶梯可以从任意一层的任意一个节点移动至其相邻层(上一层或下一层)的任意一个节点。

每次利用阶梯进行移动都需要花费固定的成本 C。
此外,还有 M个点对之间存在额外边。
如果点 u和点 v 之间存在额外边,则可以通过额外边在点 u 和点 v之间进行双向移动。
每次利用额外边进行移动都需要花费一定的成本,不同额外边的花费可能不同。

请你计算从点 1移动至点 N所需花费的最低成本。
输入格式
第一行包含一个整数 T
,表示共有 T组测试数据。
每组数据第一行包含三个整数 N,M,C。
第二行包含 N个整数 li,表示每个点所在的具体层数。
接下来 M行,每行包含三个整数 u,v,w,表示点 u 和点 v 之间存在一条额外边,成本为 w。
输出格式
每组数据输出一行答案,具体格式为 Case #x: y,其中 x为组别编号(从 1 开始),y 为所需花费的最低成本。如果无法到达点 N,则为 −1
数据范围
1≤T≤20,
0≤N,M≤105,
1≤C≤103,
1≤li≤N,
1≤u,v≤N,
u≠v,
1≤w≤104。
输入样例:

2
3 3 3
1 3 2
1 2 1
2 3 1
1 3 3
3 3 3
1 3 2
1 2 2
2 3 2
1 3 4

输出样例:

Case #1: 2
Case #2: 3

建立虚拟原点 ,来达到有当前层跳转到相邻层对的操作

int n,m,c,idx,layer[N];
bool st[N];int dist[N];
int h[N],e[N],ne[N],w[N];

void add( int a , int b , int c ){
    e[idx] = b , w[idx] = c , ne[idx] = h[a] , h[a] = idx++;
}

void init(){
    idx = 0;
    memset( h , -1 , sizeof h );
    memset( st , 0 , sizeof st );
    memset( dist , 0x3f , sizeof dist );
}

int dijkstra(){
    dist[1] = 0;
    priority_queue< PII , vector<PII> , greater<PII> >q;
    q.push( { 0 , 1 } );
    while( q.size() ){
        int ver = q.top().se;
        q.pop();

        if( st[ver] ) continue;
        st[ver] = 1;

        for( int i = h[ver] ; i != -1 ; i = ne[i] ){
            int j = e[i];
           if( dist[j] > dist[ver]+w[i] ){
                dist[j] = dist[ver]+w[i];
                q.push( {dist[j],j} );
            }

        }
    }
    if( dist[n] == INF ) return -1;
    else return dist[n];
}

int main(){
    int _;
    scanf("%d",&_);
    rep( k , 1 , _+1 ){
        init();
        scanf("%d%d%d",&n,&m,&c);
        rep( i , 1 , n+1 ) scanf("%d",&layer[i]);
        while( m-- ){
            int u,v,val;
            scanf("%d%d%d",&u,&v,&val);
            add( u , v, val );
            add( v , u, val );
        }
        int sk = n+3;
        rep( i , 1 , n+1 ){
            add( i , layer[i]+n , 0 );
            add( layer[i]+n+1 , i , c );
            add( layer[i]+n-1 , i , c );
        }
        printf("Case #%d: %d\n",k,dijkstra());
    }
    return 0;
}

规划最短路HDU3416 待补充

给定一个 n 个点 m条边的有向图。
点的编号 1∼n。
图中可能包含重边和自环。
现在,我们要从点 A前往点 B,请你同时规划出尽可能多的最短路线,要求任意两条路线均不含公共边。

输出可以同时规划出的最短路线的最大数量。
输入格式
第一行包含整数 T
,表示共有 T组测试数据。
每组数据第一行包含两个整数 n,m。
接下来 m行,每行包含三个整数 a,b,c,表示存在一条从 a 到 b 的边,长度为 c。
最后一行包含两个整数 A
和 B,表示起始点和目标点。
输出格式
每行输出一个答案,一个整数表示可以同时规划出的最短路线的最大数量。
数据范围
1≤T≤65,
2≤n≤1000,
0≤m≤105,
1≤a,b≤n,
1≤c≤1000,
1≤A,B≤n,
A≠B。
输入样例:

3
7 8
1 2 1
1 3 1
2 4 1
3 4 1
4 5 1
4 6 1
5 7 1
6 7 1
1 7
6 7
1 2 1
2 3 1
1 3 3
3 4 1
3 5 1
4 6 1
5 6 1
1 6
2 2
1 2 1
1 2 2
1 2

输出样例:

2
1
1

0或1HDU4370

给定一个 n×n 的矩阵 Cij1≤i,j≤n)

我们想找到一个 n×n的 01 矩阵 Xij(1≤i,j≤n)。Xij

应满足以下条件:

X12+X13+…+X1n=1。
X1n+X2n+…Xn−1n=1。

对于每个 i(1<i<n),满足 ∑Xki(1≤k≤n)=∑Xij (1≤j≤n)
例如,当 n=4时,需满足:

X12+X13+X14=1
X14+X24+X34=1
X12+X22+X32+X42=X21+X22+X23+X24
X13+X23+X33+X43=X31+X32+X33+X34

现在,我们想知道你能得到的 ∑Cij×Xij(1≤i,j≤n)的最小值。

输入格式
输入包含多组测试数据。
每组数据第一行包含整数 n。
接下来 n行,每行 n 个整数,用来描述矩阵 Cij。
输出格式
每个测试数据输出一行结果,表示你能得到的 ∑Cij×Xij(1≤i,j≤n)的最小值。
数据范围
输入最多包含 35组数据。
2≤n≤300,
0≤Cij≤105。
输入样例:

4
1 2 4 10
2 0 1 1
2 2 0 5
6 3 1 2

输出样例:

3

还是有点不太清楚
复习链接

int n, cir, ans, g[M][M], d[M];
bool vis[M];
void djkstra(int s) {
    memset(d, 0x3f, sizeof(d));
    memset(vis, false, sizeof(vis));
    d[s] = 0;
    for (int i = 1; i <= n; i++) {
        int t = -1;
        for (int j = 1; j <= n; j++) {
            if (!vis[j] && (t == -1 || d[t] > d[j])) {
                t = j;  
            }           
        }
        vis[t] = true;
        for (int j = 1; j <= n; j++) {
            d[j] = min(d[j], d[t] + g[t][j]);
            if (j == s && t != s) cir = min(cir, d[t] + g[t][j]); 
        }
    }
}
int main() {
    while (~scanf("%d", &n)) {
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                scanf("%d", &g[i][j]);
            } 
        } 
        cir = INF;
        djkstra(1);
        ans = d[n]; 
        int t = cir;
        cir = INF;
        djkstra(n);
        t += cir; 
        printf("%d\n", min(ans, t)); 
    }
    return 0;
}

排队布局POJ3169

当排队等候喂食时,奶牛喜欢和它们的朋友站得靠近些。

农夫约翰有 N头奶牛,编号从 1 到 N,沿一条直线站着等候喂食。

奶牛排在队伍中的顺序和它们的编号是相同的。

因为奶牛相当苗条,所以可能有两头或者更多奶牛站在同一位置上。
如果我们想象奶牛是站在一条数轴上的话,允许有两头或更多奶牛拥有相同的横坐标。

一些奶牛相互间存有好感,它们希望两者之间的距离不超过一个给定的数 L。
另一方面,一些奶牛相互间非常反感,它们希望两者间的距离不小于一个给定的数 D。
给出 ML条关于两头奶牛间有好感的描述,再给出 MD条关于两头奶牛间存有反感的描述。

你的工作是:如果不存在满足要求的方案,输出-1;如果 1
号奶牛和 N 号奶牛间的距离可以任意大,输出-2;否则,计算出在满足所有要求的情况下,1 号奶牛和 N号奶牛间可能的最大距离。
输入格式
第一行包含三个整数 N,ML,MD。
接下来 ML行,每行包含三个正整数 A,B,L,表示奶牛 A 和奶牛 B 至多相隔 L的距离。
再接下来 MD行,每行包含三个正整数 A,B,D,表示奶牛 A 和奶牛 B 至少相隔 D的距离。
输出格式
输出一个整数,如果不存在满足要求的方案,输出-1;
如果 1号奶牛和 N 号奶牛间的距离可以任意大,输出-2;
否则,输出在满足所有要求的情况下,1 号奶牛和 N号奶牛间可能的最大距离。
数据范围
2≤N≤1000,
1≤ML,MD≤104,
1≤L,D≤106

输入样例:

4 2 1
1 3 10
2 4 20
2 3 3

输出样例:

27

差分约束

int n,m1,m2,idx,cnt[N];
bool st[N];int dist[N];
int h[N],ne[N],e[N],w[N];

void add( int u , int v , int val ){
    e[idx] = v, w[idx] = val , ne[idx] = h[u] , h[u] = idx++;
}

bool spfa( int x ){
    memset(dist,0x3f,sizeof dist);
    memset(st,0,sizeof st);
    memset(cnt,0,sizeof cnt);
    queue<int>q;
    rep( i , 1 , x+1 )
    {
        st[i] = 1;
        dist[i] = 0;
        q.push( i );
    }
    while( q.size() ){
        int t = q.front();
        q.pop();

        st[t] = 0;
        for( int i = h[t] ; i != -1 ; i = ne[i]){
            int j = e[i];
            if( dist[j] > dist[t]+w[i] ){
                dist[j] = dist[t] + w[i];
                cnt[j] = cnt[t]+1;
                if( cnt[j] >= n ) return true;

                if( !st[j] )
                {
                    q.push( j );
                    st[j] = 1;
                }
            }
        }
    }

    return false;
}

int main(){
    memset( h , -1 , sizeof h );
    scanf("%d%d%d",&n,&m1,&m2);
    rep( i , 1 , n ) add( i+1 , i , 0 );
    while( m1-- ){
        int u,v,val;
        scanf("%d%d%d",&u,&v,&val);
        if( u > v ) swap( u , v );
        add( u , v , val );
    }
    while( m2-- ){
        int u,v,val;
        scanf("%d%d%d",&u,&v,&val);
        if( u > v ) swap( u , v );
        add( v , u , -val );
    }

    if( spfa(n) ) puts("-1");
    else {
        spfa(1);

        if( dist[n] == INF ) printf("-2\n");
        else printf("%d\n",dist[n]);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
并查集是一种常用的数据结构,用于管理一个不相交集合的数据。在并查集中,每个元素都有一个父节点指向它所属的集合的代表元素。通过查找和合并操作,可以判断两个元素是否属于同一个集合,并将它们合并到同一个集合中。 在解决某些问题时,可以使用并查集进行优化。例如,在区间查询中,可以通过优化并查集的查询过程,快速找到第一个符合条件的点。 对于拆边(destroy)操作,一般的并查集无法直接实现这个功能。但是可以通过一个巧妙的方法来解决。首先,记录下所有不会被拆除的边,然后按照逆序处理这些指令。遇到拆边操作时,将该边重新加入并查集中即可。 在实现并查集时,虽然它是一种树形结构,但只需要使用数组就可以实现。可以通过将每个元素的父节点记录在数组中,来表示元素之间的关系。通过路径压缩和按秩合并等优化策略,可以提高并查集的效率。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [「kuangbin带你飞」专题五并查集专题题解](https://blog.csdn.net/weixin_51216553/article/details/121643742)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [并查集(详细解释+完整C语言代码)](https://blog.csdn.net/weixin_54186646/article/details/124477838)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值