hdu 6071 Lazy Running(spfa+同余最短路)

Lazy Running

 

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 524288/524288 K (Java/Others)
Total Submission(s): 1186    Accepted Submission(s): 496

 

 

Problem Description

In HDU, you have to run along the campus for 24 times, or you will fail in PE. According to the rule, you must keep your speed, and your running distance should not be less thanK meters.

There are 4 checkpoints in the campus, indexed as p1,p2,p3 and p4. Every time you pass a checkpoint, you should swipe your card, then the distance between this checkpoint and the last checkpoint you passed will be added to your total distance.

The system regards these 4 checkpoints as a circle. When you are at checkpoint pi, you can just run to pi−1 or pi+1(p1 is also next to p4). You can run more distance between two adjacent checkpoints, but only the distance saved at the system will be counted.




Checkpoint p2 is the nearest to the dormitory, Little Q always starts and ends running at this checkpoint. Please write a program to help Little Q find the shortest path whose total distance is not less thanK.

 

 

Input

The first line of the input contains an integer T(1≤T≤15), denoting the number of test cases.

In each test case, there are 5 integers K,d1,2,d2,3,d3,4,d4,1(1≤K≤1018,1≤d≤30000), denoting the required distance and the distance between every two adjacent checkpoints.

 

 

Output

For each test case, print a single line containing an integer, denoting the minimum distance.

 

 

Sample Input

 

1
2000 600 650 535 380

 

Sample Output

2165

 

Hint


The best path is 2-1-4-3-2.

 

 

题意:

一个环1->2->3->4->1 人在任意一个点都可以走向附近的的两个点,人从2出发,最后回到2,问求出路程>=k的最小的人走的路程

 

解析:

w=min(d​1,2​​,d​2,3​​),那么对于每一种方案,均可以通过往返跑w这条边使得距离增加2w。也就是说,如果存在距离为k的方案,那么必然存在距离为k+2w的方案。

设dis_{i,j}表示从起点出发到达ii,距离模2w为j时的最短路,那么根据dis_{2,j}​​解不等式即可得到最优路线。

时间复杂度O(wlogw)。

也就是说,假设我们将任意一条长度大于k的回路(从2出发回到2)称为可行路径,那么任意一条可行路径加上2w一定还是一条可行路径,所有可行路径中,最短的是k,最长的为 k + 2 * n * w,(n趋近正无穷),显然我们不可能求出所有的可行路径来,由于所有可行路径长度中都含有2w(有可能含0个),我们可以考虑按对2w的余数分块,这样我们只要求出%2w == 0的最短可行路径,%2w == 1的最短可行路径。。。一直到%2w == 2w - 1的最短可行路径,然后在这2w条可行路径中找一条最短的就是答案了(因为这条一定是所有可行路径中最短的那条)

 

这里用同余最短路的原因是在对%2w余数为j的数,互相都可以通过加减2w来得到,所以只需要找到%2w=j这一块中的最小的那个数,就可以得到解了

 

然后问题就是如何求出%2w同余的所有可行路径中最短的那条了,用dis[i][j]表示从2号点出发到达i,长度%2w为j的最短路径的长度(注意这里不一定是可行路径,长度可以小于K,因为我们可以最后再添几个2w使其刚好大于K,并且添加2w以后并不会改变其长度%2w == j的性质),这个数组可以用dijkstra的思想求出来,注意要加堆优化。

参考:http://blog.csdn.net/lxy767087094/article/details/76723787

 

这个题目的大致的思路梳理一下就是这样的:

题目要求求>k的2->2的最短路,直接上SPFA的话,肯定不行,因为状态量就只有vis[]

那么按照题目所求的最短路%2w,一定等于某一个小于2w的数,那么我们就可以用2w的余数来标记状态量,来找各个余数下的最短路,注意这里找最短路的比较一定是要把他扩大到>k(用2w扩展)再比较,这样才能找到解。又因为这个算法的复杂度为O(w*logw),所以跟选取的除数w有很大的关系,并且这里还要满足+2w一定能回到2,所以我们只能从2的附近找,这样满足上述条件的除数就是在2两边的路径中的较小的那条边,这样+k*2w无论k多大,我们都可以回到2

类似题目点击打开链接

 

 

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define INF 6446744073709551616
using namespace std;
typedef long long int ll;

typedef struct node
{
	int d;
	int mm;
}node;

int d[5],w;
ll dist[5][60010],k;
int visit[5][60010];
ll map[5][5];

ll increase(ll res)  //对小于K的路径进行扩充使得变成%w余数不变且大于k的满足以上条件最小的数
{
	ll rr=res;
	if(res<k)
	{
		if((k-res)%w) rr=res+((k-res)/w+1)*w;
		else rr=res+((k-res)/w)*w;
	}
	return rr;
}

void spfa(node s)   
{
	memset(visit,0,sizeof(visit));
	for(int i=1;i<=4;i++)
	{
		for(int j=0;j<w;j++)
		{
			dist[i][j]=INF;
		}
	}
	queue<node> mq;

	mq.push(s);
	visit[s.d][s.mm]=1;
	dist[s.d][s.mm]=0;
	while(mq.size())
	{
		node tmp=mq.front();
		mq.pop();
		
		for(int i=1;i<=4;i++)
		{
			if(map[tmp.d][i]==INF) continue;  //若两个点间没有路则continue;
			ll res1=dist[tmp.d][tmp.mm]+map[tmp.d][i];  //2->当前该点tmp.d的最短路+tmp.d->i的最短路
			ll res2=dist[i][res1%w];       //2->i的与res1同余的最短路
			/*ll rr1,rr2;
			rr1=increase(res1);   //对他们进行最小扩充
			rr2=increase(res2);*/

			if(res2>res1)
			{

				dist[i][res1%w]=res1;
				if(visit[i][res1%w]==0)
				{
					node p;
					p.d=i;
					p.mm=res1%w;
					mq.push(p);
					visit[p.d][p.mm]=1;
				}
			}
			
		}
		visit[tmp.d][tmp.mm]=0;
	}
}

int main()
{
	int t;

	scanf("%d",&t);
	while(t--)
	{
		scanf("%lld",&k);
		for(int i=1;i<=4;i++)
			scanf("%d",&d[i]);
		w=min(d[1],d[2]);
		w=w*2;
		for(int i=1;i<=4;i++)
		{
			for(int j=1;j<=4;j++)
			{
				map[i][j]=INF;
			}
		}
		map[1][2]=map[2][1]=d[1];
		map[2][3]=map[3][2]=d[2];
		map[3][4]=map[4][3]=d[3];
		map[4][1]=map[1][4]=d[4];
		node s;
		s.d=2;
		s.mm=0;
		spfa(s);

		ll Min=INF;
		for(int i=0;i<w;i++)  //在2->2的不同余数的路中找最短路
		{
			ll tmp=increase(dist[2][i]);
			if(tmp<Min)
				Min=tmp;
		}
		printf("%lld\n",Min);
	}
	return 0;
}

 

 

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值