ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

202104-4 校门外的树

2022-03-07 03:00:20  阅读:229  来源: 互联网

标签:int max 门外 long 因子 cal 202104 dp


第一次学习DP,写起来感觉很吃力。

本题的思路参考(其实就是照搬)了这篇文章:CCF-CSP 202104-4 校门外的树(DP/好题) - 脂环 - 博客园 (cnblogs.com)

思路

使用一个数组dp来储存每一步的结果,dp[i]表示在第1个和第 i 个障碍之间存在的方案数。若以第 i 个障碍的坐标与第 j 个障碍的坐标作为等差数列的首/尾元素,则cal(i,j)为可能的等差数列公差的数量。

根据题目所给的归纳定义,可以给出递推公式:

\[dp[i]=\sum_{j=1}^{i-1}(dp[j]\times cal(j,i)) \]

关键是计算cal的值。如果自左向右的算,时间复杂度过高,而且无法利用之前步骤得到的数据。所以自右向左的算。先计算 cal(i-1,i) ,计算两个障碍间的距离 d ,然后遍历 d 的所有因子并将其标记为已使用(因子的话在预处理的时候打表)。对小于d 的所有因子,每种因子都对应一种方案。然后计算 cal(i-2,i) ,更新 d 值为障碍 i 和 i-2 间的距离,此时就要用到之前的数据了。假设之前标记了因子 x ,说明如果按照 x 来定义等差数列,那么就要在 i-1 的障碍处种树,这样的话与题设就冲突了,所以在遍历d的因子时,若因子在之前被标记过了,就需要忽略它。

代码

#include<iostream>
#include<string.h>
#include<vector>
#define n_max 1000
#define f_max 100000
#define mod (int)(1e9+7)

using namespace std;

//障碍物数量
int n;
//障碍物坐标
int pos[n_max + 1];
//dp[i]表示a[1]到a[i]的方案数
long long dp[n_max + 1];
//因数表
vector<int> v[f_max + 1];
//标志表
bool flag[f_max + 1];


//打表,求所有数的因数
void Sieve() {
	for (int i = 1; i <= f_max; i++) {
		for (int j = i; j <= f_max; j += i) {
			v[j].push_back(i);
		}
	}
}



void input() {
	cin >> n;
	for (int i = 1; i < n + 1; i++) {
		cin >> pos[i];
	}
}



//以x,y两障碍的坐标为等差数列首尾的值,计算这样的话有几种等差数列
long long cal(int x, int y) {
	long long count = 0;

	//两障碍间的距离
	int d = pos[y] - pos[x];
	for (vector<int>::iterator j = v[d].begin(); j != v[d].end(); j++) {
		int factor = *j;
		if (!flag[factor]) {
			if (factor != d)
				count++;
			flag[factor] = true;
		}
	}
	return count;
}



int main(void) {
	//打表
	Sieve();
	//初始化pos
	memset(pos, 0, (n_max + 1) * sizeof(int));
	memset(dp, 0, (n_max + 1) * sizeof(long long));
	memset(flag, false, (f_max + 1) * sizeof(bool));
	//读输入
	input();


	//DP求解
	dp[1] = 1;
	for (int i = 2; i <= n; i++) {
		//将flag恢复
		memset(flag, false, (f_max + 1) * sizeof(bool));
		for (int j = i - 1; j >= 1; j--) {
			dp[i] = (dp[i] + (dp[j] * cal(j, i)) % mod) % mod;
		}
	}

	cout << dp[n] << endl;

	return 0;
}

改进

对原文的思路进行了一些小的改进。原文的代码使用了C++STL库的set容器,用于储存、查找被标记的因子。但是set查找的时间复杂度是对数时间,所以原文的代码在 oj 中的运行时间大于900ms;为了改进,我使用了一个大的bool数组储存每个因子的标记情况,这样的话就可以随机访问数组来使查找等的时间降到常数。经过改进,程序在 oj 中的运行时间降到了100ms左右。

标签:int,max,门外,long,因子,cal,202104,dp
来源: https://www.cnblogs.com/biginsulator/p/15974349.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有