程序设计思维与实践 Week14 Blog

本文介绍两道编程题目,一是寻找网格图中十字叉的最少填充时间,涉及矩阵操作与优化算法;二是基于斐波那契数列的线性递推式求解,采用矩阵快速幂算法解决大规模计算问题。

一、大纲

本周作业与实验题目如下:

  • Q老师与十字叉
  • Q老师的考验

二、逐个击破

1.Q老师与十字叉

题目描述

  Q老师 得到一张 n 行 m 列的网格图,上面每一个格子要么是白色的要么是黑色的。
  Q老师认为失去了 十字叉 的网格图莫得灵魂. 一个十字叉可以用一个数对 x 和 y 来表示, 其中 1 ≤ x ≤ n 并且 1 ≤ y ≤ m, 满足在第 x 行中的所有格子以及在第 y 列的 所有格子都是黑色的
  例如下面这5个网格图里都包含十字叉
在这里插入图片描述
  第四个图有四个十字叉,分别在 (1, 3), (1, 5), (3, 3) 和 (3, 5).
  下面的图里没有十字叉
在这里插入图片描述
Q老师 得到了一桶黑颜料,他想为这个网格图注入灵魂。 Q老师 每分钟可以选择一个白色的格子并且把它涂黑。现在他想知道要完成这个工作,最少需要几分钟?

  1. Input

  第一行包含一个整数 q (1 ≤ q ≤ 5 * 10^4) — 表示测试组数
  对于每组数据:
  第一行有两个整数 n 和 m (1 ≤ n, m ≤ 5 * 10^4, n * m ≤ 4 * 10^5) — 表示网格图的行数和列数
  接下来的 n 行中每一行包含 m 个字符 — ‘.’ 表示这个格子是白色的, '’ 表示这个格子是黑色的
  保证 q 组数据中 n 的总和不超过 5 * 10^4, n
m 的总和不超过 4 * 10^5

  1. Output

  答案输出 q 行, 第 i 行包含一个整数 — 表示第 i 组数据的答案

题目分析

  • 这是一道单纯的模拟题。没有复杂的算法。首先是矩阵的输入。在输入的过程中记录每一行黑色格子的个数。这样输入完成之后挑出黑色格子数最多的一行进行填充,答案一定在黑色块最多的行和列。在处理完行之后进行列的处理(因为处理行的过程中将会对列产生影响),过程于上述类似。
  • 空间的粗略计算。假设开常量数组, 5 * 10^4 * 5 * 10^4 = 25 * 10^8 4B -> 即1 * 10^10B。给定内存空间262144KB,即在2e5KB-3e5KB之间,显然不够。如果用动态数组,那么4 * 10^5 * 4B=1.6 * 10^6B,足够。

错误总结
  初始思路的错误在于,黑色格子数最多的行可能有很多,这样填充不同的行对列的影响是不同的,可能使得列需要的填充不能达到最少。使用最朴素的方法。即输入过程中记录每行每列白色格子的值,这样对整个矩阵遍历一遍便可以知道所有十字叉的答案,取其中最小者即可。
  题目的全部代码如下:

#include<iostream>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#include<vector>
using namespace std;
const int N = 5*1e4 +10;
int n,m,cntx,cnty;
char str[N];
vector<int> v[N];
int row[N],column[N];
 
int main()
{
	int q;
	scanf("%d",&q);
	while(q--)
	{
		memset(row,0,sizeof(row));
		memset(column,0,sizeof(column));
		scanf("%d%d",&n,&m);
		cntx=0,cnty=0;
		for(int i=0;i<n;i++)v[i].clear();
		for(int i=0;i<n;i++)
		{
			scanf("%s",str);
			getchar();
			
			for(int j=0;j<m;j++)
			{
				if(str[j]=='*')
				{
					row[i]++;
					column[j]++;
					v[i].push_back(j);
					cnty= cnty>=column[j]? cnty:column[j];
				}
			}
			cntx = cntx>=row[i]? cntx:row[i];
		}
		
		int ans=n+m-cntx-cnty;
		bool flag1=false;
		for(int i=0;i<n && !flag1;i++)
		{
			if(row[i]!=cntx) continue;//答案一定在黑色块最多的行与列 
			for(int j=0;j<m && !flag1;j++)
			{
				if(column[j]!=cnty) continue;
				bool flag2 = false;
				for(int k=0;k<v[i].size();k++)
				if(v[i][k]==j)
				{
					flag2=1;
					break;
				}
				if(!flag2)
				{
					ans--;
					flag1=1;
				}
			}
		}
		printf("%d\n",ans);		
	}
	return 0;
} 

2.Q老师的考验

题目描述

  Q老师 对数列有一种非同一般的热爱,尤其是优美的斐波那契数列。
  这一天,Q老师 为了增强大家对于斐波那契数列的理解,决定在斐波那契的基础上创建一个新的数列 f(x) 来考一考大家。数列 f(x) 定义如下:
  当 x < 10 时,f(x) = x;
  当 x ≥ 10 时,f(x) = a0 * f(x-1) + a1 * f(x-2) + a2 * f(x-3) + …… + a9 * f(x-10),ai 只能为 0 或 1。
Q老师 将给定 a0~a9,以及两个正整数 k m,询问 f(k) % m 的数值大小。

  1. Input

  输出文件包含多组测试用例,每组测试用例格式如下:
  第一行给定两个正整数 k m。(k < 2e9, m < 1e5)
  第二行给定十个整数,分别表示 a0~a9。

  1. Output

  对于每一组测试用例输出一行,表示 f(k) % m 的数值大小。

题目分析

  题目给定的是线性递推式,如果使用普通方法求每一个f(x)则复杂度是O(10*K),无法接受
  如何优化呢?至少现在我们能想到,f(n)一定能表示成f(0)…f(9)的函数,如果这样不好理解,可以想到我们的高中学过数列,给定数列的初始项,和递推关系,有时候要求其通项公式,如果得到了通项公式,则求f(x)就是O(1)的,那么问题就转换成了如何求f(x)的通项公式
  其实我们可以把数升级到矩阵,每一次递推就相当于一次矩阵运算,多次递推就相当于迭代矩阵运算,具体的,我们可以推导出如下结果:
在这里插入图片描述

  • 计算矩阵多次幂

  借助普通快速幂的思想,我们也可以得到矩阵快速幂,只不过一次相乘的复杂度从O(1)变成了 O(N3)O(N^3)O(N3),虽然 O(N3)O(N^3)O(N3)看起来复杂度很高,但主要在于N不会很大,并且logK也不会很大(logK等于乘法的次数)
  具体如何实现:封装矩阵类,重载乘法运算符,动态分配矩阵内存(比静态更灵活),快速幂的单位元变成了单位矩阵

  题目的全部代码如下:

#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<map>
using namespace std;
int k,m;
struct Matrix
{
    int x[10][10];
    Matrix operator*(const Matrix & t) const{
        Matrix tmp;
        for(int i=0;i<10;i++)
            for(int j=0;j<10;j++)
            {
                tmp.x[i][j]=0;
                for(int k=0;k<10;k++)
                {
                    tmp.x[i][j]+=x[i][k]*t.x[k][j] % m;
                    tmp.x[i][j]%=m;
                }
            }
        return tmp;
    }

    Matrix () {memset(x,0,sizeof(x));}
    Matrix (const Matrix & t) {memcpy(x,t.x,sizeof(x));}
};

Matrix quick_pow(Matrix a,int x)
{
    Matrix tmp;
    for(int i=0;i<10;i++) tmp.x[i][i]=1;
    while(x)
    {
        if(x&1) tmp=tmp*a;
        a=a*a;
        x>>=1;
    }
    return tmp;
}

int main()
{
    while(~scanf("%d%d",&k,&m))
    {
       Matrix mat;
       for(int i=0;i<=9;i++) scanf("%d",&mat.x[0][i]);
       for(int i=1;i<=9;i++) mat.x[i][i-1]=1;
       mat=quick_pow(mat,k-9);
       int now=0;
       for(int i=0;i<10;i++)
            now=(now+mat.x[0][i]*(9-i))%m;
        printf("%d\n",now);
    }
    return 0;
}
基于数据驱动的 Koopman 算子的递归神经网络模型线性化,用于纳米定位系统的预测控制研究(Matlab代码实现)内容概要:本文围绕“基于数据驱动的Koopman算子的递归神经网络模型线性化”展开,旨在研究纳米定位系统的预测控制问题,并提供完整的Matlab代码实现。文章结合数据驱动方法Koopman算子理论,利用递归神经网络(RNN)对非线性系统进行建模线性化处理,从而提升纳米级定位系统的精度动态响应性能。该方法通过提取系统隐含动态特征,构建近似线性模型,便于后续模型预测控制(MPC)的设计优化,适用于高精度自动化控制场景。文中还展示了相关实验验证仿真结果,证明了该方法的有效性和先进性。; 适合人群:具备一定控制理论基础和Matlab编程能力,从事精密控制、智能制造、自动化或相关领域研究的研究生、科研人员及工程技术人员。; 使用场景及目标:①应用于纳米级精密定位系统(如原子力显微镜、半导体制造设备)中的高性能控制设计;②为非线性系统建模线性化提供一种结合深度学习现代控制理论的新思路;③帮助读者掌握Koopman算子、RNN建模模型预测控制的综合应用。; 阅读建议:建议读者结合提供的Matlab代码逐段理解算法实现流程,重点关注数据预处理、RNN结构设计、Koopman观测矩阵构建及MPC控制器集成等关键环节,并可通过更换实际系统数据进行迁移验证,深化对方法泛化能力的理解。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值