AtCoder Grand Contest 044 A,B

A - Pay to Win

代码看起来长,但大部分都是复制粘贴的。

核心思路:逆向思维

从0到n有很多种考虑不好想。

我们从n到0来考虑:

4中操作:

除以2,除以3,除以5,+-1.

前三种操作必须n是2/3/5的倍数,否则必须通过第四种操作加或减到最近的一个倍数然后执行除法操作。//(为什么只考虑最近的倍数呢?显然,你加减到不是最近的倍数,我们完全可以先到最近的倍数然后执行除法,再加减,到想要的位置。比如   (5+1)/3+1 =3   (5+4)/3=3  前者只需要2次+1后者则需要4次,优劣立判)

然后由于是要到0,所以我们尽可能的让数成倍缩小,(或者通过减1达到相同效果),比较两者花费较少的一个。

然后用dp记录到某个数的最小花费,但dp开不下,我们用BFS处理,记忆化一下,即记录在队列里的数,开个map维护到某个数的最小花费。

执行BFS次数等于 2,3,5相乘凑成N的个数。大约是60^3 小于1e6  复杂度完全可以接受。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
const double PI= acos(-1.0);
const int M = 1e5+7;

int head[M],cnt;
void init(){cnt=0,memset(head,0,sizeof(head));}
struct EDGE{int to,nxt,w;}ee[M*2];
void add(int x,int y,int w){ee[++cnt].nxt=head[x],ee[cnt].w=w,ee[cnt].to=y,head[x]=cnt;}


struct node{
	ll a,b;
};
int main()
{
	ios::sync_with_stdio(false);
  	cin.tie(0);
  	int t;
	cin>>t;
	while(t--)
	{
		ll n,A,B,C,D;
		cin>>n>>A>>B>>C>>D;
		queue<node>q;
		q.push(node{n,0});
		ll mi=2e18;
		map<ll,ll>mp;
		while(q.size())
		{
			node tp=q.front();q.pop();
			tp.b=mp[tp.a];
			mp.erase(tp.a);
			//cout<<tp.a<<" "<<tp.b<<"  --- "<<endl;
			if(tp.a==1)
			{
				mi=min(mi,tp.b);
				continue;
			}
			ll z=tp.a/2;
			ll e;
			if(tp.a-z<=1e9)e=D*(tp.a-z);else e=1e18;
			ll w=tp.b+min(A+D*(tp.a%2),e);
			if(mp.find(z)!=mp.end())mp[z]=min(mp[z],w);
			else mp[z]=w,q.push(node{z,w});
			
			z=(tp.a+1)/2;
			if(tp.a-z<=1e9)e=D*(tp.a-z);else e=1e18;
			w=tp.b+min(A+D*(z*2-tp.a),e);
			if(mp.find(z)!=mp.end())mp[z]=min(mp[z],w);
			else mp[z]=w,q.push(node{z,w});
			
			z=(tp.a+2)/3;
			if(tp.a-z<=1e9)e=D*(tp.a-z);else e=1e18;
			w=tp.b+min(B+D*(z*3-tp.a),e);
			if(mp.find(z)!=mp.end())mp[z]=min(mp[z],w);
			else mp[z]=w,q.push(node{z,w});
				
			z=(tp.a+4)/5;
			if(tp.a-z<=1e9)e=D*(tp.a-z);else e=1e18;
			w=tp.b+min(C+D*(z*5-tp.a),e);
			if(mp.find(z)!=mp.end())mp[z]=min(mp[z],w);
			else mp[z]=w,q.push(node{z,w});
			if(tp.a>=3)
			{
				z=tp.a/3;
				if(tp.a-z<=1e9)e=D*(tp.a-z);else e=1e18;
				ll w=tp.b+min(B+D*(tp.a%3),e);
				if(mp.find(z)!=mp.end())mp[z]=min(mp[z],w);
				else mp[z]=w,q.push(node{z,w});
			}
			if(tp.a>=5)
			{
				z=tp.a/5;
				if(tp.a-z<=1e9)e=D*(tp.a-z);else e=1e18;
				ll w=tp.b+min(C+D*(tp.a%5),e);
				if(mp.find(z)!=mp.end())mp[z]=min(mp[z],w);
				else mp[z]=w,q.push(node{z,w});
			}
		}
		cout<<mi+D<<endl;
	} 
	return 0;
}

B - Joker

开一个数组d记录刚开始时,每个位置最快离开剧场所需要的时间。

每次有一个人离开时,会对一些位置的人造成影响,由于影响只会从上下左右四个位置传播,我们用BFS取更新每次人离开造成的影响(即更新d数组)。加上优化后,每次更新一个位置会让一个位置的d减一。

由于初始所有d的值是n^3级别。

所以复杂度同样是n^3级别。

具体细节看代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
const double PI= acos(-1.0);
const int M = 500+7;

int x[M*M],y[M*M];
int dx[10]={-1,1,0,0},dy[10]={0,0,-1,1};
int vs[M][M];//i,j位置当前时刻 是否还有人 
int d[M][M];//i,j位置的人,最快离开剧场所需要的时间 
struct node{
	int x,y;
};
int main()
{
	ios::sync_with_stdio(false);
  	cin.tie(0);
  	int n;
  	cin>>n;
  	for(int i=1;i<=n*n;i++){
  		int z;
  		cin>>z;
  		x[i]=(z-1)/n+1;
  		y[i]=(z-1)%n+1;
	}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			d[i][j]=min(min(i-1,n-i),min(j-1,n-j)),vs[i][j]=1;
	int ans=0;
	for(int i=1;i<=n*n;i++)
	{
		ans+=d[x[i]][y[i]];
		vs[x[i]][y[i]]=0;
		queue<node>q;
		q.push(node{x[i],y[i]});
		while(q.size())
		{
			node tp=q.front();q.pop();
			int tx=tp.x,ty=tp.y;
			for(int j=0;j<4;j++)
			{
				int px=tx+dx[j],py=ty+dy[j];
				if(px<1||px>n||py<1||py>n||d[tx][ty]+vs[tx][ty]>=d[px][py])	continue;//px,py 先到 tx,ty 是否更优,如果不优没必要拓展下去
			 	d[px][py]=d[tx][ty]+vs[tx][ty]; 
				q.push(node{px,py});
			}
		}
	}
	cout<<ans<<endl;
	return 0;
}

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值