1186:方程的解数(友好详解)

本文讨论了如何通过双向深度优先搜索算法高效求解n元高次方程在给定范围内整数解的数量,降低至O(150³*2)的时间复杂度。
摘要由CSDN通过智能技术生成

描述
已知一个n元高次方程:

其中:x1, x2,...,xn是未知数,k1,k2,...,kn是系数,p1,p2,...pn是指数。且方程中的所有数均为整数。
假设未知数1 <= xi <= M, i=1,,,n,求这个方程的整数解的个数。
1 <= n <= 6;1 <= M <= 150

方程的整数解的个数小于231。
★本题中,指数Pi(i=1,2,...,n)均为正整数。
输入
第1行包含一个整数n。第2行包含一个整数M。第3行到第n+2行,每行包含两个整数,分别表示ki和pi。两个整数之间用一个空格隔开。第3行的数据对应i=1,第n+2行的数据对应i=n。
输出
仅一行,包含一个整数,表示方程的整数解的个数。


分析

给定N和M,有N个系数k 1到k n ,和N个指数p1到pN ,有N个未知数x1 , x2 , . . . , xN  ,每个未知数可能的范围为[1, M],求满足题目给出方程的解的个数。

乍一看最简单的思路便是暴力枚举,对于每一个未知数xi (1<i<N),尝试[1-M]里的所有整数值,带入方程,满足方程则解的数量加一,这种思路可以通过dfs实现,时间复杂度为O ( Mⁿ ) ,考虑最坏情况下的时间复杂度为O ( 150^6) ,显然直接枚举会超时。

对于这种指数级复杂度的算法,有一种非常有用的方法来降低时间复杂度——就是使用双向DFS,两边一起搜索,再中间判断状态是否符合 (meet-in-middle)

双向DFS将变量分为2部分,假设有N个未知数,第一个dfs枚举前1-N/2个未知数,对于每一种可能,计算出:

并且把计算结果存储在map中(map的key为sum1,value为前一半dfs sum1出现次数),这样之后使用sum1的结果时才够快。定义

map<int, int> dic;
dic[sum]++;

后一个dfs枚举后N/2+1-N个未知数,对每一种状态计算出

if (dic.count(-sum2)) ans += dic[-sum2]

这样通过两个dfs,该题最坏情况下的复杂度下降到O ( 150³  ∗ 2 ) ,不过需要使用map存储sum1的结果,实际上是用空间换时间。


代码
#include <iostream>
#include <map>
#include <cstring>
using namespace std;
int N, M;
int k[8], x[8], p[8], ans, mid;

long pow(long x, long y)
{
	long sum = x;
	for (long i = 1; i < y; i++)
	{
		sum *= x;
	}
	return sum;
}

void dfs_left(int steps, long sum, map<long, int>& dic)
{
	if (steps == mid+1)
	{
		dic[sum]++;
		return;
	}
	for (int i = 1; i <= M; i++)//x -> 1-M
	{
		x[steps] = i;
		dfs_left(steps + 1, sum + k[steps] * pow(x[steps], p[steps]), dic);
	}
}

void dfs_right(int steps, long sum, map<long, int>& dic)
{
	if (steps == N + 1)
	{
		if (dic.count(-sum))
			ans += dic[-sum];
		return;
	}
	for (int i = 1; i <= M; i++)
	{
		x[steps] = i;
		dfs_right(steps + 1, sum + k[steps] * pow(x[steps], p[steps]), dic);
	}
}


int main()
{

	while (cin >> N)
	{
		ans = 0;
		map<long, int> dic;
		cin >> M;
		for (int i = 1; i <= N; i++)
		{
			cin >> k[i] >> p[i];
		}
		mid = N / 2;
		dfs_left(1, 0L, dic);
		dfs_right(mid+1, 0L, dic);
		cout << ans << endl;
	}

	return 0;
}

给个赞和关注吧

  • 34
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值