cf 236 div2 E Strictly Positive Matrix 矩阵阶乘和图上路径(计数)(可达)(最优值)问题

博客讨论了如何将矩阵问题转化为图论问题,特别是如何利用Floyd算法判断图中是否所有点相互可达,从而解决cf 236 div2 E题目的条件。文章提到了矩阵A的幂次B表示路径计数,并解释了在存在自环的情况下,只需检查图的可达性来确定答案。同时,它还引用了其他图相关问题的转换例子。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

官方题解:

402E - Strictly Positive Matrix / 403C - Strictly Positive Matrix

Let's look at the matrix a as a connectivity matrix of some graph with n vertices. Moreover, if aij > 0, then we have directed edge in the graph between nodes (i, j). Otherwise, if aij = 0 that graph does not contains directed edge between pair of nodes (i, j). Let b = ak. What does bij means? bij is the number of paths of length exactly k in our graph from vertex i to vertex j. Let pos is an integer, such thata[pos][pos] > 0. That is, we have a loop in the graph. So, if from the vertex pos achievable all other vertexes and vice versa, from all other vertices reachable vertex pos, then the answer is YES, otherwise the answer is NO. If reachability is missing, it is clear that for anyk akipos = 0. If reachability there, we will be able to reach self-loop, use self-loop "to twist", and after that we will go to some another vertex.

大概题意:给定一个矩阵A(n*n的),对角线为1,其它位置为0或1.B= A^k,问是否存在某个k使得B中每个元素都为1.

矩阵阶乘和图上路径计数问题的转换:

(矩阵的元素为A[i][j] = 1代表图上点i可达点j,特殊的A[i][i] = 1。表示点i存在自环。

转化为图的问题。对应的图为:

n个点,A[i][j] = 1,则i和j之间连边,否者不连。

则B = A^k,B[i][j]代表的含义为,从i到j的路径长度为k的路径的个数。


由于本题中对角线为1,即任意点存在子环,则如果存在从i到j的长度为k 的路径,则存在从i到j长度为k+1,k+2.。。的路径。

所以只需判断可到达即可,即任一点可到达其他所有点。可用floyd,判强连通为1,或判任意点都和同一点在一个环上即可(必要条件)。

本题是判断是否存在,则用必要条件去判断即可

floyd的解法和判任意点都和同一点在一个环上的解法

int n;
bitset<2010>f[2010];
int main ()
{
    RI(n);
    REP(i, n) REP(j, n)
    {
        int x;
        RI(x);
        f[i][j] = !!x;
    }
    REP(k, n) REP(i, n)
    if (f[i][k]) f[i] |= f[k];
    int fla = 1;
    REP(i, n) REP(j, n) if (!f[i][j])
    {
        fla = 0; break;
    }
    if (fla) puts("YES");
    else puts("NO");
    return 0;
}
int n;
bool f[2010][2010];
bool used[2010], rused[2010];
void dfs(int u)
{
    used[u] = 1;
    REP(i, n) if (f[u][i] && !used[i]) dfs(i);
}
void rdfs(int u)
{
    rused[u] = 1;
    REP(i, n) if (f[i][u] && !rused[i]) rdfs(i);
}
int main ()
{
    RI(n);
    REP(i, n) REP(j, n)
    {
        int x;
        RI(x);
        f[i][j] = !!x;
    }

    dfs(0);
    rdfs(0);
    int fla = 1;
    REP(i, n) if (!used[i] || !rused[i])///都为1说明成环
        fla = 0; break;

    if (fla) puts("YES");
    else puts("NO");
    return 0;
}


下题是图上路径计数问题转化成矩阵阶乘问题:

ac自动机中求长度为L的特定字符串的个数, AC自动机 其实 就是创建了一个状态的转移图(也是一个图),思想很重要

详见:http://www.cnblogs.com/kuangbin/p/3164106.html

中的

POJ 2778 DNA Sequence

HDU 2243 考研路茫茫——单词情结


另:fzu一题,Problem 2173 Nostop

竟然可以求最优值,涨了姿势。(从点1到点n的经过k条边,的最短路径值问题)

正确性,还待理解(只要满足结合律???)

ps:本题中不允许在一点停留,所以初始化对角线为INF。这样就没办法建立单位1矩阵,所以在快速幂中,第一个是赋值,之后才是乘法

//#pragma warning (disable: 4786)
//#pragma comment (linker, "/STACK:16777216")
//HEAD
#include <cstdio>
#include <ctime>
#include <cstdlib>
#include <cstring>
#include <queue>
#include <string>
#include <set>
#include <stack>
#include <map>
#include <cmath>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
//LOOP
#define FE(i, a, b) for(int i = (a); i <= (b); ++i)
#define FD(i, b, a) for(int i = (b); i>= (a); --i)
#define REP(i, N) for(int i = 0; i < (N); ++i)
#define CLR(A,value) memset(A,value,sizeof(A))
#define CPY(a, b) memcpy(a, b, sizeof(a))
#define FC(it, c) for(__typeof((c).begin()) it = (c).begin(); it != (c).end(); it++)
//INPUT
#define RI(n) scanf("%d", &n)
#define RII(n, m) scanf("%d%d", &n, &m)
#define RIII(n, m, k) scanf("%d%d%d", &n, &m, &k)
#define RS(s) scanf("%s", s)
//OUTPUT
#define WI(n) printf("%d\n", n)
#define WS(s) printf("%s\n", s)

typedef long long LL;
const LL INF = 1LL * 1e18;
const double eps = 1e-10;
const int maxn = 100010;
const int MOD = 1000000007;

struct Metrix{
    LL tab[55][55];
    int n;
    Metrix(int n)
    {
        this->n = n;
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                tab[i][j] = INF;
    }
    Metrix operator*(Metrix a)
    {
        Metrix ret(n);
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                for (int r = 1; r <= n; r++)
                    ret.tab[i][j] = min(ret.tab[i][j], tab[i][r] + a.tab[r][j]);
        return ret;
    }
};
int n, T, m, k;
Metrix quick_pow(Metrix a, int k)
{
    Metrix ret(n);
    int st = 0;
    while (k)
    {
        if (k & 1)
        {
            if (st) ret = ret * a;
            else ret = a;
            st = 1;
        }
        k >>= 1;
        a = a * a;
    }
    return ret;
}
int main ()
{
    RI(T);
    while (T--)
    {
        RIII(n, m, k);
        Metrix tmp(n);
        for (int i = 0; i < m; i++)
        {
            int x, y;
            LL z;
            cin >> x >> y >> z;
            tmp.tab[x][y] = min(tmp.tab[x][y], z);
        }
        Metrix ans = quick_pow(tmp, k);
        if (ans.tab[1][n] == INF) ans.tab[1][n] = -1;
        cout << ans.tab[1][n] << endl;
    }
    return 0;
}
/***
1
3 2 2
1 2 2
2 3 3

1
5 5 3
1 2 1
2 5 1
1 3 10
3 4 10
4 5 10
*/




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值