标签:设计 元组 class can style 剪枝 ret one bool
Description:
就是给你一个数,你可以把它自乘,也可以把他乘或除以任意一个造出过的数,问你最多经过多少次操作能变换成目标数
思路:这题真的不怎么会啊。n = 20000,每一层都有很多个扩展状态,裸宽搜会被T,启发式函数又设计不出来……
看了一个Vjudge上的代码才知道这题怎么写。
就是每一个状态是由最多两个数转化而来的,所以可以把两个数看做一个状态。
用一个多元组$node(x,y,g,h)$表示状态,$x, y$分别表示两个数中的较大数和较小数,然后$g$表示转换成当前的状态需要多少步,$h$表示大数$x$转换到大于等于目标状态至少还要多少步。
启发式函数就是当前步数+预期至少需要的步数,即$g+h$
再用一个哈希表把二元组$(x,y)$与转换到这个状态需要几步对应起来,这样可以完成去重。当然也可以用$map$实现,但按照poj的尿性,很可能TLE。。
然后加几个剪枝,排除以下多余状态:
1.如果$x > 2*n$,这个都能理解吧。
2.如果$x=y$,因为该状态和一个$x$的状态对未来的贡献是等价的,反正自乘自除也能达到一样的效果,不管$y$取什么数,都比$x$与$y$相等时更优。
3.如果$x > n$ 并且 $y = 0$,因为这样的话该状态永远达不到$x=n$。
4.如果$n $ $mod$ $gcd(x,y) != 0$,因为这样的状态不管怎么乘怎么除,也永远达不到$x=n$。
5.如果$(x,y)$已经在哈希表里了且对应的$g$更小,这个也都能理解吧。
这样的话就应该能过了。
然后款搜的时候要注意下,枚举出一个二元组能变换出来的所有可能的二元组,这个具体可以看代码。
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<queue> 5 using namespace std; 6 const int N = 30007, SIZE = 1e6 + 10; 7 int n; 8 struct node{ 9 int x, y, g, h; 10 bool operator < (const node &a)const{ 11 return g + h == a.g + a.h ? h > a.h : g + h > a.g + a.h; 12 } 13 }; 14 struct Node{ 15 int to, next, w; 16 }; 17 struct hash_map{ 18 int head[N], now; 19 Node a[SIZE]; 20 bool insert(int sta, int w){ 21 int x = sta % N; 22 for(int i = head[x]; i; i = a[i].next){ 23 if(a[i].to == sta){ 24 if(a[i].w <= w) return 0; 25 a[i].w = w; return 1; 26 } 27 } 28 a[++now] = {sta, head[x], w}; 29 head[x] = now; 30 return 1; 31 } 32 }dict; 33 priority_queue<node> heap; 34 node now; 35 int gcd(int a, int b){ return b ? gcd(b, a % b) : a;} 36 void che(int x, int y){ 37 if(x < y) swap(x, y); 38 if(x > 2 * n) return ; 39 if(x > n && y == 0) return ; 40 if(x == y) return ; 41 if(n % gcd(x, y)) return; 42 if(!dict.insert(x * 50000 + y, now.g + 1)) return; 43 int h = 0, tx = x; 44 while(tx < n) h++, tx <<= 1; 45 heap.push({x, y, now.g + 1, h}); 46 } 47 void A_star(){ 48 heap.push({1, 0, 0, 0}); 49 while(!heap.empty()){ 50 now = heap.top(); heap.pop(); 51 if(now.x == n || now.y == n){ 52 printf("%d\n", now.g); break; 53 } 54 int a[2] = {now.x, now.y}; 55 for(int i = 0; i < 2; i++) 56 for(int j = i; j < 2; j++) 57 for(int k = 0; k < 2; k++){ 58 int b[2] = {a[0], a[1]}; 59 b[k] = a[i] + a[j]; 60 che(b[0], b[1]); 61 } 62 che(now.x - now.y, now.y); 63 che(now.x, now.x - now.y); 64 } 65 } 66 int main(){ 67 scanf("%d", &n); 68 A_star(); 69 return 0; 70 }
标签:设计 元组 class can style 剪枝 ret one bool
原文地址:https://www.cnblogs.com/Rorshach/p/9352275.html