Power Hungry Cow[POJ1945]

欢迎大家访问我的老师的OJ———caioj.cn

题面描述

传送门
数据加强版

思路

这道 A* ⁡ \operatorname{A*} A*题貌似用 map ⁡ \operatorname{map} map会被卡,

手打 hash ⁡ \operatorname{hash} hash吧。

我们要用hash来判重。

避免不必要的重复状态。

struct hashmap
{
	bool v[N<<1];
	struct edge
	{
		int x,y,next,p;
		edge(){}
	}a[N<<1];int len;int last[N<<1];
	hashmap(){}
	inline bool ins(int p,int x,int y)
	{
		int u=(x*y)%mod;
		for(int k=last[u];k;k=a[k].next)
		{
			if(a[k].x==x&&a[k].y==y)
			{
				if(a[k].p>p){a[k].p=p;return true;}
				return false;
			}
		}
		a[++len].x=x,a[len].y=y;a[len].next=last[u];last[u]=len;a[len].p=p;
		return true;
	}
}h;

貌似用 exbsgs ⁡ \operatorname{exbsgs} exbsgs hash ⁡ \operatorname{hash} hash表会被卡。

为了避免状态重复,我们进行一些剪枝:

  1. 保证 a ≥ b a\ge b ab,这样可以在hash表中避免重复。
  2. 保证 a , b a,b a,b小于数据范围
  3. 保证 n m o d &ThinSpace;&ThinSpace; gcd ⁡ ( a , b ) = = 0 n \mod \gcd(a,b)==0 nmodgcd(a,b)==0,时才能继续进行。

简要证明一下第3条

n m o d &ThinSpace;&ThinSpace; gcd ⁡ ( a , b ) ! = 0 n\mod \gcd(a,b)!=0 nmodgcd(a,b)!=0因为无论如何, a , b a,b a,b怎么乘怎么除都不可能等于 n n n.

估价函数为,把 a a a不断自加直至 ≥ n \ge n n所需的次数(注意 a ≥ b a\ge b ab

显然实际所需次数不会小于这一次数。(此处借鉴算法进阶)

接着就是直接暴力枚举一下状态就行了。

AC code

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
const int N=1e6;
const int mod=999983;
const int inf=32767;
struct node
{
	int a,b,val,cnt;
	node(){}
	node(int a,int b,int val,int cnt):a(a),b(b),val(val),cnt(cnt){} 
	bool operator <(const node a)const{return val==a.val?cnt>a.cnt:val>a.val;}
};
priority_queue<node>q;
int n;
int gcd(int a,int b){return b?gcd(b,a%b):a;}
inline int calc(int a,int b)
{
	int val=0;
	for(;a<n;a*=2)val++;
	return val;
}
struct hashmap
{
	bool v[N<<1];
	struct edge
	{
		int x,y,next,p;
		edge(){}
	}a[N<<1];int len;int last[N<<1];
	hashmap(){}
	inline bool ins(int p,int x,int y)
	{
		int u=(x*y)%mod;
		for(int k=last[u];k;k=a[k].next)
		{
			if(a[k].x==x&&a[k].y==y)
			{
				if(a[k].p>p){a[k].p=p;return true;}
				return false;
			}
		}
		a[++len].x=x,a[len].y=y;a[len].next=last[u];last[u]=len;a[len].p=p;
		return true;
	}
}h;
void add(int a,int b,int cnt)
{
	if(a<b)swap(a,b);
	if(a>inf||b>inf)return ;
	if(n%gcd(a,b))return ;
	bool bk=h.ins(cnt,a,b);
	if(bk)q.push(node(a,b,calc(a,b)+cnt,cnt));
}
int astar()
{
	add(1,0,0);h.len=0;
	while(!q.empty())
	{
		node t=q.top();q.pop();
		if(t.a==n||t.b==n)return t.cnt;
		add(t.a*2,t.b,t.cnt+1);
		add(t.a,t.b*2,t.cnt+1);
		if(t.a)add(t.a*2,t.a,t.cnt+1);
		if(t.b)add(t.b*2,t.b,t.cnt+1);
		if(t.a-t.b>=0)add(t.a-t.b,t.a,t.cnt+1),add(t.a-t.b,t.b,t.cnt+1);
		if(t.b-t.a>=0)add(t.b-t.a,t.a,t.cnt+1),add(t.b-t.a,t.b,t.cnt+1);
		add(t.a+t.b,t.b,t.cnt+1);
		add(t.a+t.b,t.a,t.cnt+1);
	}
}
int main()
{
	scanf("%d",&n);printf("%d\n",astar());
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值