【洛谷】【P3758】可乐(状压dp)

 传送门:可乐        状压dp


题目

加里敦星球的人们特别喜欢喝可乐。因而,他们的敌对星球研发出了一个可乐机器人,并且放在了加里敦星球的 1 号城市上。这个可乐机器人有三种行为: 停在原地,去下一个相邻的城市,自爆。它每一秒都会随机触发一种行为。现在给加里敦星球城市图,在第 0 秒时可乐机器人在 1 号城市,问经过了 t 秒,可乐机器人的行为方案数是多少?


输入格式

第一行输入两个正整数 N,M。N 表示城市个数,M 表示道路个数。

接下来 M 行每行两个整数 u,v,表示 u,v 之间有一条道路。保证两座城市之间只有一条路相连,且没有任何一条道路连接两个相同的城市。

最后一行是一个整数 tt,表示经过的时间。


输出格式

输出可乐机器人的行为方案数,答案可能很大,请输出对 2017取模后的结果。


题解

1. 数据结构定义
结构体 Edge:用于存储图中的每个节点及其直接相邻的节点(即边的目标节点)。每个节点通过一个向量 to 存储与之相连的邻居节点。
dp 数组:是一个动态规划表,dp[i][j] 代表从某个起始节点经过 i 步,可以到达节点 j 的路径数量。这里使用了 1000005 和 31,足以存放需要的状态。
2. 算法思路
输入和图的构建:

首先从输入中获取节点的数量 n 和边的数量 m。接着读取每一条边,并构建一个无向图,存储在 edge 数组中。
最后读取 t,表示我们需要经过的步数。
动态规划计算:

初始化 dp[0][1] = 1,表示从节点 1 开始,经过 0 步到达节点 1 自身的路径数量为 1。
然后,设置一个变量 ans 用于累计经过 t 步能到达的不同节点数量,初始为 1。
状态转移:

使用双重循环,外层循环遍历步数 i 从 1 到 t,内层循环遍历所有节点 j 从 1 到 n。
如果 dp[i-1][j] 不等于 0,说明在 i-1 步时可以到达节点 j,接下来:
更新答案 ans:将当前节点 j 的邻居数量加上路径数量,邻居数量为 edge[j].to.size(),即 j 可以到达的节点数。(edge[j].to.size() + 1) 表示可以通过 j 自身(即循环回去)以及其邻居节点进行探索。
对于每个与 j 直接相连的邻接节点 k,执行状态转移 dp[i][edge[j].to[k]] += dp[i-1][j],增加预测到达其邻居 k 的路径数量(通过节点 j)。
最后,为了计算从 j 自身开始的路径,需要将 dp[i][j] 更新为 dp[i-1][j] 的值,以便包括未发生变化的情况。

#include <iostream>
#include <vector>
using namespace std;
int dp[1000005][31];
struct Edge{
    vector<int>to;
}edge[31];
int n,m,t;
void fun(){
    int ans=1;
    dp[0][1]=1;
    for(int i=1;i<=t;i++){
        for(int j=1;j<=n;j++){
            if(dp[i-1][j]!=0) {
                ans=(ans+dp[i-1][j]*(edge[j].to.size()+1))%2017;
                for (int k = 0; k < edge[j].to.size(); k++) {
                    dp[i][edge[j].to[k]] += dp[i - 1][j];
                    dp[i][edge[j].to[k]]%=2017;
                }
                dp[i][j] += dp[i - 1][j];
                dp[i][j]%=2017;
            }
        }
    }
    cout<<ans;
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        int u,v;
        cin>>u>>v;
        edge[u].to.push_back(v);
        edge[v].to.push_back(u);
    }
    cin>>t;
    fun();
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值