P1763 题解
tangtianyao0123 · · 题解
这是五年级蒟蒻的第一篇题解,望管理员通过
题目传送门
分析
对于
-
x_1, x_2...x_n \in Z -
-
- 在满足
2 条件时,x_n 最小思路
- 不能贪心:
因为可能在选取数后剩下的数无法分拆 - 不能广搜:
在记录状态时空间会爆 - 不能深搜:
深搜可能无底洞的搜下去,暴时间
那怎么办呢?
我要介绍一种新东西:迭代加深搜索
迭代加深搜索(IDDFS)
迭代加深搜索本质是深搜,但他限定了深度,一旦当前搜索深度大于或等于限定深度,就立刻回溯,如果当前限制的深度有答案有直接结束,如果没有就放宽限制
所以伪代码大概长这样:
void dfs(int dep, ...){//dep表示当前深度
if(dep > maxdep) return ;
if(dep == maxdep){
//do sth
return ;
}
for(/*当前状态转移出的新状态y*/){
dfs(dep + 1, y);
}
}
int main(){
...
for(maxdep = 1; ; maxdep++){
dfs(0, ...);
if(有结果){
//do sth
return 0;
}
}
}
有人就会问:在限定新深度时,浅深度的状态会被枚举很多次,会不会浪费时间导致
我的回答是:这个得具体情况具体分析,当一个状态能转移出的状态不多时,在枚举新限定深度时,需要记录上一层状态;但如果转移状态是指数级的增长时,就没必要,因为前几层的状态在后几层来说就是九牛一毛,记状态也快不了多少。
迭代加深弥补了广搜的空间开销太大,也弥补了深搜的无底洞
对于每次转移,就是在剩下的
对于 样例都过不了的代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
int a, b, dep;
vector<int> v, ans;
bool f;
int gcd(int a, int b){
return b ? gcd(b, a % b) : a;
}
void dfs(int x, int a, int b, int l){
if(x == dep){
if(!a){
if(ans.empty() || v.back() < ans.back()){
ans = v;
}
f = 1;
}
return ;
}
for(int i = max(l + 1, (b + a - 1) / a); i <= 1e7; i++){
int d = gcd(a * i - b, i * b);
v.push_back(i);
dfs(x + 1, (a * i - b) / d, i * b / d, i);
v.pop_back();
}
}
signed main(){
cin >> a >> b;
int x = gcd(a, b);
a /= x, b /= x;
for(dep = 1; ; dep++){
dfs(0, a, b, 1);
if(f){
for(int x : ans){
cout << x << ' ';
}
break;
}
}
return 0;
}
我们需要优化
剪枝1
对于没一次枚举
剪枝2
枚举最后一层工作量极大,可以直接求。
根据前面两个剪枝,可以写出以下代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
int a, b, dep, cnt = 0;
vector<int> v, ans;
bool f;
int gcd(int a, int b){
return b ? gcd(b, a % b) : a;
}
void dfs(int x, int a, int b, int l){
if(x == dep - 1){
if(a == 1){
if(b > v.back()){
v.push_back(b);
if(ans.empty() || v.back() < ans.back()){
ans = v;
}
v.pop_back();
f = 1;
}
}
return ;
}
for(int i = max(l + 1, b / a); i <= 1e7 && a * i < (dep - x + 1) * b; i++){
int d = gcd(a * i - b, i * b);
v.push_back(i);
if((a * i - b) / d * 1e7 >= i * b / d){
dfs(x + 1, (a * i - b) / d, i * b / d, i);
}
v.pop_back();
}
}
signed main(){
cin >> a >> b;
int x = gcd(a, b);
a /= x, b /= x;
for(dep = 1; dep < 11; dep++){
dfs(0, a, b, 1);
if(f){
for(int x : ans){
cout << x << ' ';
}
break;
}
}
return 0;
}
然后就会获得 hack数据TLE 的拥抱
如果你只想练习迭代加深,那你可以走了
剪枝3
考察最后两层搜索,它们占了主要的时间。
我们想要知道
ak = x_1+x_2 \hspace{1cm} (1) \\ bk = x_1x_2 \hspace{1cm} (2) \end{cases}
将
又因为
同时得保证,
为什么不管
因为
所以我们得枚举
AC代码
理解第一,理解第一,理解第一
#include <bits/stdc++.h>
#define int long long
using namespace std;
int a, b, dep;
vector<int> v, ans;
bool f;
int gcd(int a, int b){
return b ? gcd(b, a % b) : a;
}
void dfs(int x, int a, int b, int l){
if(x == dep - 2){
int mi = (4 * b + a * a - 1) / (a * a);
for(int k = mi; ; k++){
int delta = a * a * k * k - 4 * b * k, sd = sqrt(delta), t = -1;
if(sd * sd == delta) t = sd;
else if((sd - 1) * (sd - 1) == delta) t = sd - 1;
else if((sd + 1) * (sd + 1) == delta) t = sd + 1;
int x1 = (a * k - t) / 2, x2 = (a * k + t) / 2;
if(x2 > 1e7 || f && x2 >= ans.back()) break;
if(t <= 0 || (a * k + t) % 2 != 0 || x1 <= l) continue;
v.push_back(x1);
v.push_back(x2);
ans = v;
v.pop_back();
v.pop_back();
f = 1;
}
return ;
}
for(int i = max(l + 1, (b + a - 1) / a); i <= 1e7 && a * i < (dep - x + 1) * b; i++){
int d = gcd(a * i - b, i * b);
v.push_back(i);
if(i <= 1e7 && (a * i - b) / d * 1e7 >= i * b / d){
dfs(x + 1, (a * i - b) / d, i * b / d, i);
}
v.pop_back();
}
}
signed main(){
cin >> a >> b;
int x = gcd(a, b);
a /= x, b /= x;
if(a == 1) return cout << b, 0;
for(dep = 2; dep <= 11; dep++){
dfs(0, a, b, 1);
if(f){
for(int x : ans){
cout << x << ' ';
}
break;
}
}
return 0;
}