题意
Time Limit: 15000MS Memory Limit: 128000K
Description
已知一个n元高次方程:
k
1
x
1
p
1
+
k
2
x
2
p
2
+
.
.
.
.
.
.
+
k
n
x
n
p
n
=
0
k_1x_1^{p1}+k_2x_2^{p_2}+......+k_nx_n^{p_n}=0
k1x1p1+k2x2p2+......+knxnpn=0
其中:
x
1
,
x
2
,
.
.
.
,
x
n
是
未
知
数
,
k
1
,
k
2
,
k
n
是
系
数
,
p
1
,
p
2
,
.
.
.
p
n
是
指
数
x_1,x_2,...,x_n是未知数,k_1,k_2,k_n是系数,p_1,p_2,...p_n是指数
x1,x2,...,xn是未知数,k1,k2,kn是系数,p1,p2,...pn是指数,且方程中的所有数均为整数。
设未知数
1
<
=
x
i
<
=
M
,
i
=
1
,
.
.
.
,
n
1<=x_i<=M,i=1,...,n
1<=xi<=M,i=1,...,n,求这个方程的整数解的个数。
1
<
=
n
<
=
6
;
1
<
=
M
<
=
150
1<=n<=6;1<=M<=150
1<=n<=6;1<=M<=150
∣
k
1
M
p
1
∣
+
∣
k
2
M
p
2
∣
+
.
.
.
.
.
.
+
∣
k
n
M
p
n
∣
<
2
31
|k_1M^{p_1}|+|k_2M^{p_2}|+......+|k_nM^{p_n}|<2^{31}
∣k1Mp1∣+∣k2Mp2∣+......+∣knMpn∣<231
方程整数解的个数小于
2
31
2^{31}
231
Input
第1行包含一个整数n。第2行包含一个整数M。第3行到第n+2行,每行包含两个整数,分别表示
k
i
k_i
ki和
p
i
p_i
pi。两个整数之间用一个空格隔开。第3行的数据对应i=1,第n+2行的数据对应i=n。
Output
仅一行,包含一个整数,表示方程的整数解的个数。
Sample Input
3
150
1 2
-1 2
1 2
Sample Output
178
思路: 给定N和M,有N个系数 k 1 . . . k N k_1...k_N k1...kN,和N个指数 p 1 , . . . p N p_1,...p_N p1,...pN,有N个未知数 x 1 , x 2 , . . . , x N x_1,x_2,...,x_N x1,x2,...,xN,每个未知数可能的范围为[1, M],求满足题目给出方程的解的个数。
乍一看最简单的思路便是暴力枚举,对于每一个未知数 x i ( 1 < i < N ) x_i(1<i<N) xi(1<i<N),尝试[1-M]里的所有整数值,带入方程,满足方程则解的数量加一,这种思路可以通过dfs实现,时间复杂度为 O ( M N ) O(M^N) O(MN),考虑最坏情况下的时间复杂度为 O ( 15 0 6 ) O(150^6) O(1506),显然直接枚举会超时。
对于这种指数级复杂度的算法,有一种非常有用的方法来降低时间复杂度——就是使用双向DFS,两边一起搜索,再中间判断状态是否符合 (meet-in-middle)
双向DFS将变量分为2部分,假设有N个未知数,第一个dfs枚举前1-N/2个未知数,对于每一种可能,计算出:
s
u
m
1
=
k
1
x
1
p
1
+
.
.
.
+
k
N
/
2
x
N
/
2
p
N
/
2
sum_1=k_1x_1^{p_1}+...+k_{N/2}x_{N/2}^{p_{N/2}}
sum1=k1x1p1+...+kN/2xN/2pN/2
并且把计算结果存储在map中(map的key为sum1,value为前一半dfs sum1出现次数),这样之后使用sum1的结果时才够快。定义
map<int, int> dic;
dic[sum]++;
后一个dfs枚举后N/2+1-N个未知数,对每一种状态计算出
s
u
m
2
=
k
N
/
2
+
1
x
N
/
2
+
1
p
N
/
2
+
1
+
.
.
.
+
k
N
x
N
p
N
sum_2=k_{N/2+1}x_{N/2+1}^{p_{N/2+1}}+...+k_{N}x_{N}^{p_{N}}
sum2=kN/2+1xN/2+1pN/2+1+...+kNxNpN
此时如果有
−
s
u
m
2
=
=
s
u
m
1
-sum_2 == sum_1
−sum2==sum1则
s
u
m
1
+
s
u
m
2
=
0
sum_1+sum_2=0
sum1+sum2=0满足题目中的方程,将解的数目记录下来,dic里以及存储了sum1的结果,用C++可以写作:
if (dic.count(-sum2)) ans += dic[-sum2]
这样通过两个dfs,该题最坏情况下的复杂度下降到 O ( 15 0 3 ∗ 2 ) O(150^3*2) O(1503∗2),不过需要使用map存储sum1的结果,实际上是用空间换时间。
code v1
通过上面的思路,我直接使用了stl里的map作为哈希表,但是map底层实现是用高度平衡的binary search trees.,查找一个元素的时间是
O
(
l
o
g
N
)
O(logN)
O(logN),N为map中的数据量。在这题用map提交TLE。C++11引入了unordered_map, unordered_map查找的时间复杂度为
O
(
1
)
O(1)
O(1),可是POJ貌似不支持C++11,于是需要自己实现一个查找尽量在常数时间内的哈希表。
以下代码为使用STL中的map,提交超时,但是作为理解算法还是可以参考。
#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;
}
code_v2
自己用数组实现一个哈希表
#include <iostream>
using namespace std;
const int NUM = 10000003;
int N, M;
long k[8], x[8], p[8], ans, mid;
typedef struct
{
int sum, value;
}hash_table;
hash_table hsh[NUM];
long get_index(long x)//哈希函数:输入x,给出x在hsh的下标
{
long index = abs(x);
if (index >= NUM)
index = index % NUM;
while (hsh[index].value != 0 && hsh[index].sum != x)//线性地址再探测
{
index += 1;
if (index >= NUM)
index = index % NUM;
}
return index;
}
void insert_num(long x)//将x插入哈希表
{
long index = get_index(x);
hsh[index].sum = x;
hsh[index].value++;
}
long get_value(long x)
{
long index = get_index(x);
return hsh[index].value;
}
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)
{
if (steps == mid+1)//[0-mid]
{
insert_num(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]));
}
}
void dfs_right(int steps, long sum)
{
if (steps == N + 1)//[mid+1, N]
{
int s = get_value(-sum);
if (s)
ans += s;
return;
}
for (int i = 1; i <= M; i++)
{
x[steps] = i;
dfs_right(steps + 1, sum + k[steps] * pow(x[steps], p[steps]));
}
}
int main()
{
ans = 0;
cin >> N >> M;
for (int i = 1; i <= N; i++)
{
cin >> k[i] >> p[i];
}
mid = N / 2;
dfs_left(1, 0L);
dfs_right(mid+1, 0L);
cout << ans << endl;
return 0;
}
提交