「Codeforces113D」Museum-高斯消元

Description

有一个 n n n个点 m m m条边的无向图,两个人分别从 x , y x,y x,y出发,每个人每分钟有 p i p_i pi的概率不动, 有 1 − p i 1−p_i 1pi的概率走到随机一个相邻的点。

当他们在同一时刻选择前往同一个房间, 他们就会在那个房间相遇并停止。

求在每个点相遇的概率。

n ≤ 22 n \leq 22 n22

Solution

这道题有两种做法。

第一种做法,枚举终点,然后高斯消元即可。直接这样做是 O ( n 7 ) O(n^7) O(n7)的,显然无法通过。考虑每次修改终点只会修改 f ( i , i ) f(i,i) f(i,i)的值,所以可以把 f ( i , i ) f(i,i) f(i,i)视为常数,那么列出方程组后高斯消元可以得出 f ( i , j ) = ∑ k = 1 n A i , j , k f ( k , k ) f(i,j)=\sum_{k=1}^n A_{i,j,k} f(k,k) f(i,j)=k=1nAi,j,kf(k,k)。每次枚举终点暴力计算即可。

第二种做法,设 f ( i , j ) f(i,j) f(i,j)表示第一个人在 i i i第二个人在 j j j的期望出现次数,由于终止状态 ( i , i ) (i,i) (i,i)的出现次数只可能为 0 / 1 0/1 0/1,所以概率即期望。直接解方程即可。

注意初始点的方程为

f ( a , b ) − 1 = ∑ i = 1 n ∑ j = 1 n A ( i , j ) f ( i , j ) f(a,b)-1=\sum_{i=1}^n\sum_{j=1}^n A(i,j)f(i,j) f(a,b)1=i=1nj=1nA(i,j)f(i,j)

其余点的方程为

f ( a , b ) = ∑ i = 1 n ∑ j = 1 n A ( i , j ) f ( i , j ) f(a,b)=\sum_{i=1}^n\sum_{j=1}^n A(i,j)f(i,j) f(a,b)=i=1nj=1nA(i,j)f(i,j)

其中 A ( i , j ) A(i,j) A(i,j)表示从 ( a , b ) (a,b) (a,b)转移到 ( i , j ) (i,j) (i,j)的概率。

#include <bits/stdc++.h>
using namespace std;

inline int gi()
{
    char c = getchar();
    while(c < '0' || c > '9') c = getchar();
    int sum = 0;
    while('0' <= c && c <= '9') sum = sum * 10 + c - 48, c = getchar();
    return sum;
}

const double eps = 1e-7;
const int maxn = 25, maxm = maxn * maxn;

int n, m, a, b, deg[maxn], Id[maxn][maxn];
vector<int> to[maxn];
double p[maxn], mat[maxm][maxm], ans[maxm], f[maxn][maxn];

void pre()
{
    m = 0;
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= n; ++j)
            Id[i][j] = ++m;
    mat[Id[a][b]][m + 1] = -1;
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= n; ++j) {
            int x = Id[i][j];
            --mat[x][x];
            if (i != j) mat[x][x] += p[i] * p[j];
            for (int u : to[i])
                for (int v : to[j]) {
                    if (u == v) continue;
                    mat[x][Id[u][v]] = (1 - p[u]) * (1 - p[v]) / deg[u]  / deg[v];
                }
            for (int u : to[i]) {
                if (u == j) continue;
                mat[x][Id[u][j]] = (1 - p[u]) * p[j]/ deg[u];
            }
            for (int v : to[j]) {
                if (v == i) continue;
                mat[x][Id[i][v]] = p[i] * (1 - p[v]) / deg[v];
            }
        }
}

int dcmp(double a)
{
    if (-eps < a && a < eps) return 0;
    else if (a > 0) return 1;
    else return -1;
}

/*int gauss()
{
    for (int i = 1; i < m; ++i) {
        int p = i;
        for (int j = i + 1; j <= m; ++j)
            if (abs(mat[j][i]) > abs(mat[p][i])) p = j;
        if (p != i)
            for (int j = i; j <= m + 1; ++j) swap(mat[i][j], mat[p][j]);
        for (int j = i + 1; j <= m; ++j) {
            if (!dcmp(mat[j][i])) continue;
            double t1 = mat[j][i], t2 = mat[i][i];
            for (int k = i; k <= m + 1; ++k) mat[j][k] -= mat[i][k] * t1 / t2;
        }
    }
    for (int i = m; i >= 1; --i) {
        if (!dcmp(mat[i][i])) return 0;
        for (int j = i + 1; j <= m; ++j)
            mat[i][m + 1] -= ans[j] * mat[i][j];
        ans[i] = mat[i][m + 1] / mat[i][i];
    }
    return 1;
}*/

void gauss()
{
    for (int i = 1; i <= m; ++i) {
        int p = i;
        for (int j = i; j <= m; ++j)
            if (abs(mat[j][i]) > abs(mat[p][i])) p = j;
        if (p != i) 
            for (int j = i; j <= m; ++j) swap(mat[p][j], mat[i][j]);
        for (int j = 1; j <= m; ++j) {
            if (p == j) continue;
            double t = mat[j][i] / mat[i][i];
            for (int k = i; k <= m + 1; ++k) mat[j][k] -= mat[i][k] * t;
        }
    }
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= n; ++j)
            f[i][j] = mat[Id[i][j]][m + 1] / mat[Id[i][j]][Id[i][j]];
}

int main()
{
    n = gi(); m = gi(); a = gi(); b = gi();
    for (int u, v, i = 1; i <= m; ++i) {
        u = gi(); v = gi();
        to[u].push_back(v); ++deg[u];
        to[v].push_back(u); ++deg[v];
    }
    for (int i = 1; i <= n; ++i) scanf("%lf", &p[i]);

    pre();
    gauss();

    for (int i = 1; i <= n; ++i) printf("%.10lf ", f[i][i]);
    
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CodeForces - 616D是一个关于找到一个序列中最长的第k好子段的起始位置和结束位置的问题。给定一个长度为n的序列和一个整数k,需要找到一个子段,该子段中不超过k个不同的数字。题目要求输出这个序列最长的第k好子段的起始位置和终止位置。 解决这个问题的方法有两种。第一种方法是使用尺取算法,通过维护一个滑动窗口来记录\[l,r\]中不同数的个数。每次如果这个数小于k,就将r向右移动一位;如果已经大于k,则将l向右移动一位,直到个数不大于k。每次更新完r之后,判断r-l+1是否比已有答案更优来更新答案。这种方法的时间复杂度为O(n)。 第二种方法是使用枚举r和双指针的方法。通过维护一个最小的l,满足\[l,r\]最多只有k种数。使用一个map来判断数的种类。遍历序列,如果当前数字在map中不存在,则将种类数sum加一;如果sum大于k,则将l向右移动一位,直到sum不大于k。每次更新完r之后,判断i-l+1是否大于等于y-x+1来更新答案。这种方法的时间复杂度为O(n)。 以上是两种解决CodeForces - 616D问题的方法。具体的代码实现可以参考引用\[1\]和引用\[2\]中的代码。 #### 引用[.reference_title] - *1* [CodeForces 616 D. Longest k-Good Segment(尺取)](https://blog.csdn.net/V5ZSQ/article/details/50750827)[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^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Codeforces616 D. Longest k-Good Segment(双指针+map)](https://blog.csdn.net/weixin_44178736/article/details/114328999)[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^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值