VOJ1067 【矩阵经典7 构造矩阵】

本文探讨了一位守望者如何使用闪烁技能视察监狱的问题,通过动态规划算法求解不同技能等级下到达终点的方案数量。

任意门:https://vijos.org/records/5be95b65d3d8a1366270262b

背景

守望者-warden,长期在暗夜精灵的的首都艾萨琳内担任视察监狱的任务,监狱是成长条行的,守望者warden拥有一个技能名叫“闪烁”,这个技能可以把她传送到后面的监狱内查看,她比较懒,一般不查看完所有的监狱,只是从入口进入,然后再从出口出来就算完成任务了。

描述

头脑并不发达的warden最近在思考一个问题,她的闪烁技能是可以升级的,k级的闪烁技能最多可以向前移动k个监狱,一共有n个监狱要视察,她从入口进去,一路上有n个监狱,而且不会往回走,当然她并不用每个监狱都视察,但是她最后一定要到第n个监狱里去,因为监狱的出口在那里,但是她并不一定要到第1个监狱。

守望者warden现在想知道,她在拥有k级闪烁技能时视察n个监狱一共有多少种方案?

格式

输入格式

第一行是闪烁技能的等级k(1<=k<=10)
第二行是监狱的个数n(1<=n<=2^31-1)

输出格式

由于方案个数会很多,所以输出它 mod 7777777后的结果就行了

样例1

样例输入1

2
4
Copy

样例输出1

5
Copy

限制

各个测试点1s

提示

把监狱编号1 2 3 4,闪烁技能为2级,
一共有5种方案
→1→2→3→4
→2→3→4
→2→4
→1→3→4
→1→2→4

小提示:建议用int64,否则可能会溢出

 

题意概括:

给出可闪现的距离 K 房间个数 N,问到达终点的方案数;

解题思路:

很明显的DP,DP的转移方程也显而易见 F(N) = F(N-1)+F(N-2)+ ... + F(N-K);

找出递推式,很显然可以用矩阵来优化,并且系数为 1,So easy!

以为到这就解决问题了,太粗心啦,注意数据范围,注意数据精度!!!要用 long long

 

Ac code:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define LL long long
using namespace std;
const int MAXN = 11;
const LL Mod  = 7777777;
int N, K;

struct mat
{
    LL m[MAXN][MAXN];
}base, tmp, ans;

mat muti(mat a, mat b)
{
    mat res;
    memset(res.m, 0, sizeof(res.m));

    for(int i = 1; i <= K; i++)
    for(int j = 1; j <= K; j++){
        if(a.m[i][j]){
            for(int k = 1; k <= K; k++){
                res.m[i][k] = (res.m[i][k] + a.m[i][j]*b.m[j][k])%Mod;
//                res.m[i][k] = res.m[i][k]%Mod;
            }
        }
    }
    return res;
}

mat qpow(mat a, int n)
{
    mat res;
    memset(res.m, 0, sizeof(res));
    for(int i = 1; i <= K; i++) res.m[i][i] = 1LL;
    while(n){
        if(n&1) res = muti(res, a);
        n>>=1;
        a = muti(a, a);
    }
    return res;
}

int main()
{
    scanf("%d%d", &K, &N);
    memset(base.m, 0, sizeof(base.m));
    base.m[0][1] = 1LL;
    for(int i = 1; i <= K; i++){
        for(int j = 0; j < i; j++)
            base.m[i][1] += base.m[j][1]%Mod;
    }

    if(N <= K) printf("%lld\n", base.m[N][1]);
    else{
        memset(tmp.m, 0, sizeof(tmp.m));
        for(int i = 1; i < K; i++){
            tmp.m[i][i+1] = 1LL;
        }
        for(int i = 1; i <= K; i++) tmp.m[K][i] = 1LL;

        tmp = qpow(tmp, N-K);
        mat ans = muti(tmp, base);

        printf("%lld\n", ans.m[K][1]%Mod);
    }
    return 0;
}

 

### 旅行商问题 VOJ 题解 TSP 算法实现 旅行商问题(Traveling Salesman Problem, TSP)是一个经典的组合优化问题,目标是在给定一组城市及其两两之间的距离的情况下,找到一条访问每个城市恰好一次并返回起始城市的最短路径。TSP 是 NP 完全问题,在实际应用中通常采用动态规划、分支限界或者启发式方法求解。 #### 动态规划解决 TSP 一种常见的解决方案是基于状态压缩的动态规划。假设共有 \( n \) 个城市,定义状态 \( dp[S][i] \),其中 \( S \) 是已访问的城市集合(可以用位掩码表示),\( i \) 是当前所在城市,则 \( dp[S][i] \) 表示从起点出发经过集合 \( S \) 中的所有城市最后到达城市 \( i \) 的最小代价。 转移方程可以写成: \[ dp[S][i] = \min_{j \in S, j \neq i} (dp[S-\{i\}][j] + dist[j][i]) \] 初始条件为: \[ dp[\{0\}][0] = 0 \] 其余均为无穷大。 最终答案可以通过枚举最后一个访问的城市得到: \[ ans = \min_i(dp[(1<<n)-1][i] + dist[i][0]) \] 以下是 Python 实现代码: ```python import sys def tsp(dist): n = len(dist) INF = float('inf') # 初始化 DP 数组 dp = [[INF] * n for _ in range(1 << n)] dp[1 << 0][0] = 0 for mask in range(1 << n): for u in range(n): if not (mask & (1 << u)): continue for v in range(n): if not (mask & (1 << v)) or u == v: continue dp[mask][u] = min(dp[mask][u], dp[mask ^ (1 << u)][v] + dist[v][u]) final_mask = (1 << n) - 1 return min(dp[final_mask][i] + dist[i][0] for i in range(n)) # 示例输入矩阵 dist_matrix = [ [0, 29, 20, 21], [29, 0, 15, 17], [20, 15, 0, 28], [21, 17, 28, 0] ] result = tsp(dist_matrix) print(result) ``` 上述代码实现了基于动态规划的状态压缩算法来求解 TSP[^6]。 #### 启发式方法解决 TSP 对于较大的规模,精确算法难以在合理时间内完成计算,因此常使用近似算法或启发式方法,如遗传算法、模拟退火等。这些方法虽然无法保证最优解,但在实践中往往能够快速获得接近最优的结果。 ##### 模拟退火算法简介 模拟退火是一种随机化搜索技术,通过模仿金属冷却过程中的物理现象寻找全局最优解。其核心思想是从一个较高的温度开始逐步降温,并允许一定概率接受更差的解以跳出局部极小值。 伪代码如下: ```plaintext 初始化解 s 和温度 T while 温度 T 大于最低阈值 do 执行若干次迭代: 随机生成邻域内的新解 s' 如果 cost(s') 小于等于 cost(s) 或者 exp((cost(s) - cost(s')) / T) > rand() then 更新当前解 s <- s' 减少温度 T end while 返回最佳解 ``` 此方法适用于大规模实例下的高效求解[^7]。 --- ####
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值