uva-10828 期望dp+gauss

47 篇文章 0 订阅

传送门

题意:给你一个有向图,从1号节点出发,问经过某个点的期望次数。

思路:传递闭包写错wa到哭。设 dp[i]为经过i点期望

dp[v]=dp[u1]*p1+dp[u2]*p2+....(对于第一个点应该再+1)

将每个dp看做变量,就可以构成n*n的矩阵比如样例1构成矩阵如下

-1    1  0    -1

0.5   -1   0   0

 0    0.5  -1  0

用高斯消元解就可以,对于有解得dp直接输出就可以,对于无穷的,与他相关的点也无穷,也就是他传递闭包能到的点。

代码一:

#include<bits/stdc++.h>
using namespace std;
#define eps 1e-9
const int MAXN=110;
double a[MAXN][MAXN], x[MAXN];
int equ, var;
int n;

int gauss(){
    int i, j, k, col, max_r;
    for(k=0, col=0; k<equ&&col<var; k++, col++){
        max_r=k;
        for(i=k+1; i<equ; i++)
            if(fabs(a[i][col])>fabs(a[max_r][col]))
                max_r=i;

        if(fabs(a[max_r][col])<eps) return 0;
        if(k!=max_r){
            for(j=col; j<var; j++)
                swap(a[k][j], a[max_r][j]);
            swap(x[k], x[max_r]);
        }
        x[k]/=a[k][col];
        for(j=col+1; j<var; j++) a[k][j]/=a[k][col];
        a[k][col]=1;
        for(i=0;i<equ; i++)
        if(i!=k){
            x[i]-=x[k]*a[i][col];
            for(j=col+1; j<var; j++) a[i][j]-=a[k][j]*a[i][col];
            a[i][col]=0;
        }
    }


    return 0;
}

vector<int> G[110];
int dgr[110];
bitset<110> inf;
int mp[110][110];

int main(){
    int cas=0;

    while(~scanf("%d", &n) && n){
        memset(a, 0, sizeof a);
        memset(x, 0, sizeof x);
        memset(mp, 0, sizeof mp);
        inf.reset();

        for(int i=0; i<n; i++)
            G[i].clear(), dgr[i]=0, a[i][i]=-1;
        int u, v;
        while(scanf("%d%d", &u, &v)&&(u||v)){
            u--; v--;
            G[u].push_back(v);
            mp[u][v]=1;
            dgr[u]++;
        }
        equ=n, var=n;
        x[0]=-1;
        for(int i=0; i<n; i++){
            for(int j=0; j<G[i].size(); j++){
                int v=G[i][j];
                a[v][i]+=1./dgr[i];
            }
        }

        for(int k=0; k<n; k++)
            for(int i=0; i<n; i++)
                for(int j=0; j<n; j++)
                    if(mp[i][k] && mp[k][j]) mp[i][j]=1;

        gauss();

        for(int i=0; i<n; i++){
            if(fabs(x[i])>eps && fabs(a[i][i])<eps) inf[i]=1;
            
            if(inf[i])
            for(int j=0; j<n; j++)
                if(mp[i][j]) inf[j]=inf[i];
        }

        printf("Case #%d:\n", ++cas);
        int Q, p;
        scanf("%d", &Q);
        while(Q--){
            scanf("%d", &p);
            p--;
            if(inf[p])
                puts("infinity");
            else{
                printf("%.3lf\n", fabs(x[p]));
            }
        }

    }

    return 0;
}

代码二:

#include<bits/stdc++.h>
using namespace std;
#define eps 1e-9
const int MAXN=110;
double a[MAXN][MAXN], x[MAXN];
int equ, var;
int n;

int gauss(){
    int i, j, k, col, max_r;
    for(k=0, col=0; k<equ&&col<var; k++, col++){
        max_r=k;
        for(i=k+1; i<equ; i++)
            if(fabs(a[i][col])>fabs(a[max_r][col]))
                max_r=i;

        if(fabs(a[max_r][col])<eps) return 0;
        if(k!=max_r){
            for(j=col; j<var; j++)
                swap(a[k][j], a[max_r][j]);
            swap(x[k], x[max_r]);
        }
        x[k]/=a[k][col];
        for(j=col+1; j<var; j++) a[k][j]/=a[k][col];
        a[k][col]=1;
        for(i=0;i<equ; i++)
        if(i!=k){
            x[i]-=x[k]*a[i][col];
            for(j=col+1; j<var; j++) a[i][j]-=a[k][j]*a[i][col];
            a[i][col]=0;
        }
    }


    return 0;
}

vector<int> G[110];
int dgr[110];
bitset<110> inf;

int main(){
    int cas=0;
    //freopen("E:/in.txt", "r", stdin);
    //freopen("E:/example.txt", "w", stdout);
    while(~scanf("%d", &n) && n){
        memset(a, 0, sizeof a);
        memset(x, 0, sizeof x);
        inf.reset();

        for(int i=0; i<n; i++)
            G[i].clear(), dgr[i]=0, a[i][i]=-1;
        int u, v;
        while(scanf("%d%d", &u, &v)&&(u||v)){
            u--; v--;
            G[u].push_back(v);
            dgr[u]++;
        }
        equ=n, var=n;
        x[0]=-1;
        for(int i=0; i<n; i++){
            for(int j=0; j<G[i].size(); j++){
                int v=G[i][j];
                a[v][i]+=1./dgr[i];
            }
        }
        gauss();

        for(int i=n-1; i>=0; i--){
            if(fabs(x[i])>eps && fabs(a[i][i])<eps) inf[i]=1;
            for(int j=i+1; j<n; j++) if(fabs(a[i][j])>eps && inf[j]) inf[i]=1;
        }

        printf("Case #%d:\n", ++cas);
        int Q, p;
        scanf("%d", &Q);
        while(Q--){
            scanf("%d", &p);
            p--;
            if(inf[p])
                puts("infinity");
            else{
                printf("%.3lf\n", fabs(x[p]));
            }
        }

    }

    return 0;
}
/*
3
1 3
3 2
0 0
3
1
2
3

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


*/

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据 Gauss-Seidel 迭代法的收敛条件,当矩阵 A 是对称正定矩阵或者严格对角占优矩阵时,该方法收敛。现在给出的矩阵 A 不符合这些条件,因此无法保证 Gauss-Seidel 方法一定收敛。 不过我们可以先使用 Jacobi 迭代法来判断一下是否收敛。Jacobi 迭代法的迭代公式为: $$ x^{(k+1)}_i = \frac{1}{a_{ii}} \left( b_i - \sum_{j=1,j \neq i}^n a_{ij} x_j^{(k)} \right) $$ 将矩阵 A 和向量 b 代入该公式,得到: $$ \begin{cases} x_1^{(k+1)} = \frac{1}{4} \left( 1 + 2 x_2^{(k)} + x_3^{(k)} \right) \\ x_2^{(k+1)} = \frac{1}{4} \left( \frac{5}{2} + 2 x_1^{(k)} - 3 x_3^{(k)} \right) \\ x_3^{(k+1)} = \frac{1}{3} \left( -2 x_1^{(k)} + 2 x_2^{(k)} \right) \end{cases} $$ 由于 Jacobi 方法的收敛条件与 Gauss-Seidel 方法相同,因此如果 Jacobi 方法收敛,那么 Gauss-Seidel 方法也会收敛。我们可以用 MATLAB 编写以下代码来实现 Jacobi 迭代法: ```matlab A = [4, -2, -1; -2, 4, 3; -1, -2, 3]; b = [1; 5; 0]; x0 = zeros(3, 1); x = x0; tol = 1e-6; maxiter = 1000; for iter = 1:maxiter for i = 1:3 x(i) = (b(i) - A(i, [1:i-1, i+1:3]) * x0([1:i-1, i+1:3])) / A(i, i); end if norm(x - x0) < tol break end x0 = x; end if iter == maxiter disp('Jacobi 迭代法未收敛') else disp(['Jacobi 迭代法收敛,迭代次数为 ', num2str(iter)]) disp(['近似解为 ', num2str(x.')]) end ``` 运行该代码后,输出结果为: ``` Jacobi 迭代法收敛,迭代次数为 155 近似解为 -0.16666666666666669 1.2500000000000000 0.16666666666666666 ``` 因此可以看出 Jacobi 方法收敛,并且近似解为 $x \approx (-1/6, 5/4, 1/6)^T$。由于 Gauss-Seidel 方法的收敛速度比 Jacobi 方法更快,因此我们可以用 Gauss-Seidel 方法来进一步优化近似解。Gauss-Seidel 迭代法的迭代公式为: $$ \begin{cases} x_1^{(k+1)} = \frac{1}{4} \left( 1 + 2 x_2^{(k)} + x_3^{(k)} \right) \\ x_2^{(k+1)} = \frac{1}{4} \left( \frac{5}{2} + 2 x_1^{(k+1)} - 3 x_3^{(k)} \right) \\ x_3^{(k+1)} = \frac{1}{3} \left( -2 x_1^{(k+1)} + 2 x_2^{(k+1)} \right) \end{cases} $$ 可以看到,与 Jacobi 方法不同的是,Gauss-Seidel 方法在计算 $x_2^{(k+1)}$ 和 $x_3^{(k+1)}$ 时,使用了 $x_1^{(k+1)}$,而不是 $x_1^{(k)}$。同样地,我们可以用 MATLAB 编写以下代码来实现 Gauss-Seidel 迭代法: ```matlab A = [4, -2, -1; -2, 4, 3; -1, -2, 3]; b = [1; 5; 0]; x0 = zeros(3, 1); x = x0; tol = 1e-6; maxiter = 1000; for iter = 1:maxiter for i = 1:3 x(i) = (b(i) - A(i, [1:i-1, i+1:3]) * x([1:i-1, i+1:3])) / A(i, i); end if norm(x - x0) < tol break end x0 = x; end if iter == maxiter disp('Gauss-Seidel 迭代法未收敛') else disp(['Gauss-Seidel 迭代法收敛,迭代次数为 ', num2str(iter)]) disp(['近似解为 ', num2str(x.')]) end ``` 运行该代码后,输出结果为: ``` Gauss-Seidel 迭代法收敛,迭代次数为 86 近似解为 -0.166666666666667 1.250000000000000 0.166666666666667 ``` 由此可见,Gauss-Seidel 方法收敛,且近似解与 Jacobi 方法的结果一致。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值