题目
加里敦星球的人们特别喜欢喝可乐。因而,他们的敌对星球研发出了一个可乐机器人,并且放在了加里敦星球的 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;
}