TOJ 1551 Power Hungry Cows -- 搜索

2 篇文章 0 订阅

题目链接:http://acm.tzc.edu.cn/acmhome/problemdetail.do?&method=showdetail&id=1551

题目大意:从数对(0, 1)开始,每次可以把两个数(可以取同一个数两次)相加或相减并替换原来的任意一个数,问得到给定的p的最少操作步数。

分析:一开始想用贪心,但是很快就发现不行。。。。没办法,只能搜索。由于一次变化最多得到8个新值(可以把a, b中的任意一个变为a + a, a + b, b + b, abs(a - b)),不加很强的优化是肯定不行的。

优化1:注意a一开始为0,所以第一个非零a一定等于上一次的b,后来的a也一定在之前的b中出现过。特别地,之后的每一次a + a一定等于之前的某一个b + b,所以a + a实际上是用不着的。

优化2:其次,由于每一次变化后得到的新值new_value都是a与b的线性组合,故一定有gcd(a, b) | new_value,换言之,如果gcd(a, b)不整除p,无解。

优化3:b不可能太大,我假设的是max(a, b) <= 2p,具体证明不会(正解性也未知)。

优化4:A*算法,用step表示当前操作次数。用auxi(x)表示每次把x加倍(或减半)使x不小于(或不大于)p的最小次数,那么每次取step + auxi(max(a, b))最小的数。注意代码中那一句very very important注释,加了这一句之后速度快的不是一点点,在我自己电脑上测试p = 20000时,不加这一句时间为6312ms,加了之后为110ms。

加了这几条之后也是900+ms过的。。。

#include 
   
   
    
    
#include 
    
    
     
     
#include 
     
     
      
      
#include 
      
      
       
       
#include 
       
       
         #include 
         #include 
         
           #include 
          
            #include 
           
             #include 
            
              #include 
             
               #include 
              
                #include 
               
                 #include 
                
                  #include 
                 
                   #include 
                  
                    #define mp make_pair #define X first #define Y second #define MEMSET(a, b) memset(a, b, sizeof(a)) using namespace std; typedef unsigned int ui; typedef long long ll; typedef unsigned long long ull; typedef pair 
                   
                     pii; typedef vector 
                    
                      vi; typedef vi::iterator vi_it; typedef map 
                     
                       mii; typedef priority_queue 
                      
                        pqi; typedef priority_queue 
                       
                         , greater 
                        
                          > rpqi; typedef priority_queue 
                         
                           pqp; typedef priority_queue 
                          
                            , greater 
                           
                             > rpqp; int p, ans; set 
                            
                              coll; int auxi(int k) { int ret = 0; if (k < p) { while (k < p) { ++ret; k <<= 1; } ret += k > p; //very very important } else { while (k > p) { ++ret; k >>= 1; } ret += k < p; } return ret; } struct node { int x; int y; int step; node() { } node(int _x, int _y, int _step) : x(_x), y(_y), step(_step) { } bool operator < (const node &nd) const { return step + auxi(y) > nd.step + auxi(nd.y); } }; priority_queue 
                             
                               q; int gcd(int a, int b) { if (!b) return a; int r = a % b; while (r) { a = b; b = r; r = a % b; } return b; } inline void add_node(const node &nd) { if (coll.count(mp(nd.x, nd.y)) || nd.y > p + p) return; if (p % gcd(nd.x, nd.y) == 0) q.push(nd); } void solve() { q.push(node(0, 1, 0)); coll.insert(mp(0, 1)); coll.insert(mp(0, 0)); while (true) { node tmp = q.top(); q.pop(); if (tmp.y == p) { printf("%d\n", tmp.step); return; } //change y node new_node(tmp.x, tmp.y + tmp.y, tmp.step + 1); add_node(new_node); new_node.y = tmp.x + tmp.y; add_node(new_node); new_node.y = tmp.y - tmp.x; if (new_node.x > new_node.y) swap(new_node.x, new_node.y); add_node(new_node); //change x new_node.x = tmp.y; new_node.y = tmp.x + tmp.y; add_node(new_node); new_node.y = tmp.y + tmp.y; add_node(new_node); new_node.x = tmp.y - tmp.x; new_node.y = tmp.y; add_node(new_node); } } int main(int argc, char *argv[]) { // freopen("D:\\in.txt", "r", stdin); cin >> p; clock_t st = clock(); solve(); cout << clock() - st << endl; return 0; } 
                              
                             
                            
                           
                          
                         
                        
                       
                      
                     
                    
                   
                  
                 
                
               
              
             
            
           
          
       
      
      
     
     
    
    
   
   

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值