证明:有依赖背包结点数优化后为O(n^2)

文章详细分析了一个背包问题的动态规划解法,利用深度优先搜索策略,证明了当m较大时,核心代码的时间复杂度为O(n^2),主要通过子树节点数和数学归纳法进行论证。
摘要由CSDN通过智能技术生成

证明:有依赖背包结点数优化后为 O ( n 2 ) O(n^2) O(n2)

siz[u]表示以u为根的树的结点数
深搜过程中,siz[u]表示根结点为u的树的根结点加上前i个子树的结点数。

  1. 根结点为u的树取前i个子树的结点,取到的结点数量小于等于siz[u]
  2. 根结点为v的子树取到的结点数量小于等于siz[v]

例:
洛谷 P2014 [CTSC1997] 选课 使用结点数优化

#include<bits/stdc++.h>
using namespace std;
#define N 305
int n, m, w[N], siz[N], dp[N][N];//dp[u][i][j]:结点u的前i个孩子,最多选择j门课能获得的最大学分 
vector<int> edge[N];
void dfs(int u)
{
	dp[u][1] = w[u];//选1门课,就只能选自己 
	siz[u] = 1;
	for(int v : edge[u])
	{
		dfs(v);
		siz[u] += siz[v];
		for(int j = min(m, siz[u]); j >= 1; --j)//在树中选j门课 
			for(int k = 0; k < j && k <= siz[v]; ++k)//在子树v中选了k门课 (因为还要选v,最多选j-1门) 
				dp[u][j] = max(dp[u][j], dp[u][j-k]+dp[v][k]); 
	}
}
int main()
{
	int f;
	cin >> n >> m;
	m++;//算上0号结点 
	for(int i = 1; i <= n; ++i)
	{
		cin >> f >> w[i];
		edge[f].push_back(i);
	}
	dfs(0);
	cout << dp[0][m];
	return 0;
}

假设m很大,不考虑j的影响,其核心代码可以视作:

void dfs(int u)
{
	dp[u][1] = w[u];//选1门课,就只能选自己 
	siz[u] = 1;
	for(int v : edge[u])
	{
		dfs(v);
		siz[u] += siz[v];
		for(int j = siz[u]; j >= 1; --j)
			for(int k = 0; <= siz[v]; ++k)
				dp[u][j] = max(dp[u][j], dp[u][j-k]+dp[v][k]); 
	}
}

设根结点为u的树的结点数量为n,
证明:上述代码的复杂度为 O ( n 2 ) O(n^2) O(n2)
设结点u有k个子树1、2、3、…、k
每个子树的结点数分别为 a 1 , a 2 , . . . , a k a_1, a_2, ..., a_k a1,a2,...,ak,因此有 a 1 + a 2 + . . . + a k = n − 1 a_1+a_2+...+a_k = n-1 a1+a2+...+ak=n1
该代码中,双重循环内层语句的运行次数近似为: a 1 ∗ a 1 + ( a 1 + a 2 ) ∗ a 2 + ( a 1 + a 2 + a 3 ) ∗ a 3 + . . . + ( a 1 + . . . + a k ) ∗ a k a_1*a_1+(a_1+a_2)*a_2+(a_1+a_2+a_3)*a_3+...+(a_1+...+a_k)*a_k a1a1+(a1+a2)a2+(a1+a2+a3)a3+...+(a1+...+ak)ak
使用数学归纳法:

  1. 如果结点u的孩子都是叶子结点,则 a 1 , a 2 , . . . , a k a_1, a_2, ..., a_k a1,a2,...,ak都为1,那么语句运行次数为: 1 + 2 + . . . + k = ( 1 + k ) ⋅ k / 2 ≤ k 2 1+2+...+k =(1+k)\cdot k/2 \le k^2 1+2+...+k=(1+k)k/2k2
  2. 如果结点u的孩子不都是叶子结点,对于子树x的递归调用,语句运行次数 ≤ a x 2 \le a_x^2 ax2,因此总语句运行次数 ≤ a 1 ∗ a 1 + ( a 1 + a 2 ) ∗ a 2 + ( a 1 + a 2 + a 3 ) ∗ a 3 + . . . + ( a 1 + . . . + a k ) ∗ a k + a 1 2 + a 2 2 + . . . + a k 2 \le a_1*a_1+(a_1+a_2)*a_2+(a_1+a_2+a_3)*a_3+...+(a_1+...+a_k)*a_k+a_1^2+a_2^2+...+a_k^2 a1a1+(a1+a2)a2+(a1+a2+a3)a3+...+(a1+...+ak)ak+a12+a22+...+ak2
    将前面每项写开
    a 1 2 a_1^2 a12
    a 1 ∗ a 2 + a 2 2 a_1*a_2+a_2^2 a1a2+a22
    a 1 ∗ a 3 + a 2 ∗ a 3 + a 3 2 a_1*a_3+a_2*a_3+a_3^2 a1a3+a2a3+a32

    a 1 ∗ a k + . . . + a k 2 a_1*a_k+...+a_k^2 a1ak+...+ak2
    相加得
    a 1 ∗ ∑ i = 1 k a i + a 2 ∗ ∑ i = 2 k a i + a 3 ∗ ∑ i = 3 k a i + . . . + a k ∗ a k < a_1*\sum_{i=1}^k{a_i}+a_2*\sum_{i=2}^k{a_i}+a_3*\sum_{i=3}^k{a_i}+...+a_k*a_k < a1i=1kai+a2i=2kai+a3i=3kai+...+akak<
    a 1 ∗ ∑ i = 1 k a i + a 2 ∗ ∑ i = 1 k a i + a 3 ∗ ∑ i = 1 k a i + . . . + a k ∗ ∑ i = 1 k a i = ( ∑ i = 1 k a i ) 2 = ( a 1 + a 2 + . . . + a k ) 2 = ( n − 1 ) 2 < n 2 a_1*\sum_{i=1}^k{a_i}+a_2*\sum_{i=1}^k{a_i}+a_3*\sum_{i=1}^k{a_i}+...+a_k*\sum_{i=1}^k{a_i} = (\sum_{i=1}^k{a_i})^2 = (a_1+a_2+...+a_k)^2=(n-1)^2<n^2 a1i=1kai+a2i=1kai+a3i=1kai+...+aki=1kai=(i=1kai)2=(a1+a2+...+ak)2=(n1)2<n2
    a 1 2 + a 2 2 + . . . + a k 2 < ( a 1 + a 2 + . . . + a k ) 2 = ( n − 1 ) 2 < n 2 a_1^2+a_2^2+...+a_k^2 < (a_1+a_2+...+a_k)^2=(n-1)^2<n^2 a12+a22+...+ak2<(a1+a2+...+ak)2=(n1)2<n2
    因此语句总运行次数 < 2 n 2 <2n^2 <2n2
    因此该算法的时间复杂度为 O ( n 2 ) O(n^2) O(n2)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值