【POJ3613】Cow Relays

题目链接:https://www.acwing.com/problem/content/347/

题目大意:给定一张无向带权图 , 找出从 \(s\) 到 \(e\) 恰好经过 \(n\) 条边的最短路径

solution

不难发现 , 顶点数可能很多 , 但边数不超过 100 , 因此对于节点编号应该先离散化一下

用矩阵 \(G^r\) , 表示恰好经过 \(r\) 条边的最短路 , 其中 \(G^r[i][j]\) 表示从 \(i\) 到 \(j\) 恰好经过 \(r\) 条边的最短路径 , 显然可以得出以下推论 :
\[
G^r[i][j] = min_{k \leq n}\left\{G^p[i][k] + G^q[k][j]\right\}(r = p + q , n为节点数量)
\]
同时发现 , 这个式子满足结合律 , 因此可以用矩阵快速幂快速求出 \(G^r\) , \(G^n[s][e]\)即为题目所求

code

#include<bits/stdc++.h>
using namespace std;
template <typename T> inline void read(T &FF) {
    int RR = 1; FF = 0; char CH = getchar();
    for(; !isdigit(CH); CH = getchar()) if(CH == '-') RR = -RR;
    for(; isdigit(CH); CH = getchar()) FF = FF * 10 + CH - 48;
    FF *= RR;
}
inline void file(string str) {
    freopen((str + ".in").c_str(), "r", stdin);
    freopen((str + ".out").c_str(), "w", stdout);
}
const int N = 205;
int n, m, s, e, path[N][3], ni, num[N];
struct matrix{
    int g[N][N];
    matrix() {
        memset(g, 0x3f, sizeof(g));
        //for(int i = 1; i <= n; i++) g[i][i] = 0;
    }
    friend matrix operator * (const matrix &ai, const matrix &bi) {
        matrix res;
        for(int k = 1; k <= ni; k++)
            for(int i = 1; i <= ni; i++)
                for(int j = 1; j <= ni; j++)
                    res.g[i][j] = min(res.g[i][j], ai.g[i][k] + bi.g[k][j]);
        return res;
    }
}base;
void cpy(matrix &ai, const matrix &bi) {
    for(int i = 1; i <= ni; i++)
        for(int j = 1; j <= ni; j++)
            ai.g[i][j] = bi.g[i][j];
}
matrix Qpow(int ki) {
    if(ki == 1) return base;
    matrix hi = Qpow(ki / 2); cpy(hi, hi * hi);
    return ki & 1 ? hi * base : hi;
}
int main() {
    //file("");
    read(n), read(m), read(s), read(e);
    for(int i = 1; i <= m; i++) {
        read(path[i][0]), read(path[i][1]), read(path[i][2]);
        num[++ni] = path[i][1], num[++ni] = path[i][2];
    }
    sort(num + 1, num + ni + 1);
    ni = unique(num + 1, num + ni + 1) - num - 1;
    //for(int i = 1; i <= n; i++) base.g[i][i] = 0;
    for(int i = 1; i <= m; i++) {
        path[i][1] = lower_bound(num + 1, num + ni + 1, path[i][1]) - num;
        path[i][2] = lower_bound(num + 1, num + ni + 1, path[i][2]) - num;
        base.g[path[i][1]][path[i][2]] = base.g[path[i][2]][path[i][1]] = path[i][0];
    }
    s = lower_bound(num + 1, num + ni + 1, s) - num;
    e = lower_bound(num + 1, num + ni + 1, e) - num;
    matrix ans = Qpow(n);
    cout << ans.g[s][e] << endl;
    return 0;
}

原文地址:https://www.cnblogs.com/magicduck/p/12241818.html

时间: 2024-10-15 10:55:50

【POJ3613】Cow Relays的相关文章

「POJ3613」Cow Relays

「POJ3613」Cow Relays 传送门 就一个思想:\(N\) 遍 \(\text{Floyd}\) 求出经过 \(N\) 个点的最短路 看一眼数据范围,想到离散化+矩阵快速幂 代码: #include <cstring> #include <cstdio> #define rg register #define file(x) freopen(x".in", "r", stdin), freopen(x".out"

poj3613:Cow Relays(倍增优化+矩阵乘法floyd+快速幂)

Cow Relays Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 7825   Accepted: 3068 Description For their physical fitness program, N (2 ≤ N ≤ 1,000,000) cows have decided to run a relay race using the T (2 ≤ T ≤ 100) cow trails throughout

【POJ3613】【USACO 2007 Nov Gold】 2.Cow Relays 矩阵乘法?

题意:给你一个m条边的图,求s到t的正好用k条边的最短路. (输入k,t,s,t) 题解: 先说说暴力. 动规f[k][i][j]表示i到j经过k条边的最短路,然后外层循环k一遍遍跑最后出解. 显然大概率T. 然后有一种思路: 我们可以动规求得f[k][i][j]表示i到j经过k条边的最短路,然后再求g[i]表示从终点走i步回到终点的最短路. 这样我们就可以乱搞过了.(没写,也没细想) 再之后是正解: 我们可以利用类似于快速幂的方法求f[i][j]表示i到j正好用多少多少步. 然后思想是使用边数

【BZOJ】【1046】/【POJ】【3613】【USACO 2007 Nov】Cow Relays 奶牛接力跑

倍增+Floyd 题解:http://www.cnblogs.com/lmnx/archive/2012/05/03/2481217.html 神题啊= =Floyd真是博大精深…… 题目大意为求S到E,恰好经过N条边的最短路径(姑且称为路径吧,虽然好像已经不是了……) 总共只有大约200个点(很多点根本没走到,离散化一下即可)所以可以考虑Floyd算最短路. 引用下题解: 题目求i,j之间边数恰为N的最短路径(边可以重复走),我们知道线性代数中有:01邻接矩阵A的K次方C=A^K,C[i][j

【BZOJ-4422】Cow Confinement 线段树 + 扫描线 + 差分 (优化DP)

4422: [Cerc2015]Cow Confinement Time Limit: 50 Sec  Memory Limit: 512 MBSubmit: 61  Solved: 26[Submit][Status][Discuss] Description 一个10^6行10^6列的网格图,上面有一些牛.花和一些矩形围栏,围栏在格子的边界上,牛和花在格子里,牛只能向下或向右走,牛也不能穿过围栏和地图边界,求每头牛它能到达的花的数量.注意栅栏不会相交 Input 第一行一个数f表示矩形围栏的

【POJ】Cow Multiplication(水题)

Cow Multiplication http://poj.org/problem?id=3673 题意:输入两个数A B,比如123和45   然后算123*45这个运算是指1*4 + 1*5 + 2*4 + 2*5 + 3*4 + 3*5 = 54. 思路:水题. #include<iostream> #include<cmath> #include<cstring> using namespace std; typedef long long ll; const

【poj3615】 Cow Hurdles

http://poj.org/problem?id=3615 (题目链接) 题意 给出一张有向图,求从u到v最大边最小的路径的最大边.→_→不会说话了.. Solution 好久没写Floyd了,水一发.邻接表都不用打... 代码 // poj3615 #include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #inclu

【poj1985】 Cow Marathon

http://poj.org/problem?id=1985 (题目链接) 题意 求树上两点间最长距离.题目背景以及输入描述请见poj1984. Solution 树的直径. 代码 // poj1985 #include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<cmath> #define L

【bzoj1649】Cow Roller Coaster

傻逼dp题. dp[i][j]表示用了i长度已花费成本j所能得到的价值. 然后枚举一下铁轨随便做了. 不行就sort一下. #include<bits/stdc++.h> #define inf 1000000007 typedef long long ll; using namespace std; struct Node{int s,c,w,f;}a[100010]; ll f[1005][1005]; bool cmp(Node a,Node b){ if(a.s==b.s)return