旅行方案

解题思路分析

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;
}
posted @ 2025-04-29 19:27  季风起  阅读(7)  评论(0)    收藏  举报