Java教程

202104-4 校门外的树

本文主要是介绍202104-4 校门外的树,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

第一次学习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左右。

这篇关于202104-4 校门外的树的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!