HDU 2157 How many ways??(经典矩阵快速幂)

题意:求A经过K个点到B方案数

方法一:

1个0 1 的矩阵 A
a[i][j] = 1 表示i 到 j可达 或者说 i 到 j 有1条路 或者说i到j经过一个点的方案数 路可以重复走
 
而A2 = A* A
a[i][j] 的含义是
从i到j经过2个点的方案数

A的k次方 A[i,j]代表 i到j走k步的方案有a[i][j]

矩阵乘法的定义居然和这个模型如此契合,佩服,所以要非常熟悉矩阵乘法的具体步骤才能在这个题目中抽象出矩阵乘法可以正好实现两个定点间的所有可能情况

方法二:

动态规划的思路,状态是dp[c][u] 从起始点s到u点经过c条边的所有方法数,这种把起点看出固定,把终点看成状态之一的动态规划思路在最短路模型中已经多见。在dp(c,u)这个状态后的决策是:u,v之间有有向边,那么可以向dp(c+1,v)的状态转移。


矩阵快速幂:

//1100 KB	78 ms
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define mod 1000
struct mat{
    int a[25][25];
    void ini(){
        memset(a,0,sizeof(a));
    }
};
int n,m;
mat mapp;
int st,ed,c;
mat mul(mat &m1,mat &m2){
    mat ans;
    ans.ini();
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
    if(m1.a[i][j])
    for(int k=1;k<=n;k++)
        ans.a[i][k]=(ans.a[i][k]+m1.a[i][j]*m2.a[j][k])%mod;
    return ans;
}
int quickmul(mat m,int c){
    mat ans;
    ans.ini();
    for(int i=1;i<=n;i++) ans.a[i][i]=1;
    while(c){
        if(c&1) ans=mul(ans,m);
        m=mul(m,m);
        c>>=1;
    }
    return ans.a[st][ed];
}
int main(){

    while(scanf("%d%d",&n,&m),n+m){
        mapp.ini();
        while(m--){
            int u,v;
            scanf("%d%d",&u,&v);
            mapp.a[++u][++v]=1;
        }
        int t;
        scanf("%d",&t);
        while(t--){
            scanf("%d%d%d",&st,&ed,&c);
            st++;ed++;
            printf("%d\n",quickmul(mapp,c));
        }
    }
    return 0;
}

动态规划:

//15MS 1092K
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
#define mod 1000
int dp[25][25];
bool mapp[25][25];
int main(){
    int n,m;
    while(scanf("%d%d",&n,&m),m+n){
        memset(mapp,0,sizeof(mapp));
        while(m--){
            int u,v;
            scanf("%d%d",&u,&v);
            mapp[++u][++v]=1;
        }
        int t;
        scanf("%d",&t);
        while(t--){
            memset(dp,0,sizeof(dp));
            int st,ed,k;
            scanf("%d%d%d",&st,&ed,&k);
            st++;ed++;
            dp[0][st]=1;
            for(int c=1;c<=k;c++)
            for(int u=1;u<=n;u++)
            for(int v=1;v<=n;v++){
                if(mapp[u][v]) dp[c][v]=(dp[c][v]+dp[c-1][u])%mod;
            }
            printf("%d\n",dp[k][ed]);
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值