描述
已知一个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;
}
给个赞和关注吧