旅行方案
解题思路分析
1.问题理解:
我们需要计算从城市1出发,经过k天后又回到城市1的合法路径数量。
每条路径必须满足相邻两天所在城市不同且有完好的道路连接。
2.关键观察:
这是一个典型的动态规划问题,状态转移需要考虑道路的限制条件。
每天的选择取决于前一天的位置和可用的道路。
3.动态规划设计:
状态定义:dp[v]表示当前步数下到达城市v的方案数。
初始状态:dp[1] = 1(第0天在城市1)。
状态转移:
计算所有城市的方案数总和cnt。
对于每个城市v,计算不可行的方案数bad(包括停留在原地和走损坏的道路)。
新状态dp2[v] = 总方案数 - 不可行方案数。
结果输出:经过k次迭代后dp[1]的值即为答案。
4.优化点:
使用邻接表(unordered_set)存储损坏的道路,快速查询。
每次迭代只保留前一步的状态,空间复杂度优化为O(n)。
及时取模防止整数溢出。
5.复杂度分析:
时间复杂度:O(k*(n+m)),外层循环k次,内层对n个城市和m条边进行处理。
空间复杂度:O(n+m),存储图和dp数组。
6.样例解释:
样例1中,损坏道路只有2-3,所以有4种合法路径(如代码注释所示)。
样例2中所有道路都损坏,所以没有合法路径(输出0)。
样例3需要处理较大的k值,验证算法的正确性和效率。
#include <bits/stdc++.h>
using namespace std;
const int mod = 998244353; // 定义模数,用于结果取模
int n, m, k; // n-城市数量,m-损坏道路数量,k-旅行天数
int main() {
cin >> n >> m >> k;
// vis数组记录每个城市不可达的邻接城市(损坏的道路)
vector<unordered_set<int>> vis(n+1); // 下标从1开始
// 读入损坏的道路信息
for(int i=0; i<m; ++i){
int u, v;
cin >> u >> v;
vis[u].insert(v); // 记录u不能到达v
vis[v].insert(u); // 记录v不能到达u(无向图)
}
// dp数组,dp[v]表示当前步数下到达城市v的方案数
vector<int> dp(n+1, 0);
dp[1] = 1; // 初始状态:第0天在城市1的方案数为1
// 动态规划,计算k步后的结果
for(int i=0; i<k; i++){
int cnt = 0;
// 计算上一步所有城市的方案数总和
for(int v=1; v<=n; ++v) cnt = (cnt + dp[v]) % mod;
vector<int> dp2(n+1, 0); // 新一步的dp数组
for(int v=1; v<=n; ++v){
int bad = dp[v]; // 不能停留在原地的方案数
// 加上不能走的相邻城市的方案数(因为不能走回头路)
for(int u : vis[v]) bad = (bad + dp[u]) % mod;
// 新一步的方案数 = 总方案数 - 不可行的方案数
dp2[v] = (cnt - bad + mod) % mod; // 加mod防止负数
}
dp = move(dp2); // 更新dp数组
}
cout << dp[1] << endl; // 输出k天后回到城市1的方案数
return 0;
}