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 }