转载自http://blog.csdn.net/yasola
题目大意:
给你一个由四个节点组成的环,求从节点2出发,回到节点2的不小于k的最短路。
解题思路:
又是一个可能出现无限向下走的题目。面对这种问题有一个比较常见的处理方法就是利用同余。
题目要求的是回路,回路有这样一个性质,任意两个回路可以连接构成一个新的回路。于是任意一个回路就可以表示成x+n*y的形式,其中x和y是两个回路。现在再回到利用同余防止循环,如果我们固定y(代码中用的变量名为m),那么对于任意两个模y同余的x的效果是相同的,我们只需要保留最小的那个即可。
显然时间复杂度和y正相关,那么我们就取满足题意的最小回路作为y,然后利用最短路算法找到所有起点为1的相互关于y不同余的环,更新答案即可。在最短路的过程中,对于每一个点也只需要保留关于y不同余的路径即可。
#include<algorithm>
#include<vector>
#include<cstring>
#include<string>
#include<iomanip>
#include<cstdio>
#include<stack>
#include<iostream>
#include<map>
#include<queue>
#include<cmath>
using namespace std;
#define sf scanf
#define pf printf
#define mem(a,b) memset(a,b,sizeof(a));
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define bug1 puts("bug1");
#define bug2 puts("bug2");
#define bug3 puts("bug3");
#define N 1200
#define M 100020
#define mod 100000
#define ULL unsigned long long
#define LL long long
#define inf 0x3f3f3f3f
const int maxp=60010;
LL k;
LL ans;
LL G[5][5];
bool vis[5][maxp];
LL dist[5][maxp];
struct Node{
int u;
LL dis;
Node(int u,LL d):u(u),dis(d){}
};
int m;
void spfa(int s){
mem(vis,false);
mem(dist,inf);
vis[s][0]=true;
dist[s][0]=0;
queue<Node>q;
q.push(Node(s,0));
while(!q.empty()){
int u=q.front().u;
LL now_dis=q.front().dis;q.pop();
vis[u][now_dis%m]=false;
for(int i=-1;i<2;i+=2){
int v=(u+i+4)%4;
LL next_dis=now_dis+G[u][v],next_p=next_dis%m;
if(v==s){
if(next_dis<k)ans=min(ans,((k-1-next_dis)/m+1)*m+next_dis);
else ans=min(next_dis,ans);
}
if(dist[v][next_p]>next_dis){
dist[v][next_p]=next_dis;
if(!vis[v][next_p]){
vis[v][next_p]=true;
q.push(Node(v,next_dis));
}
}
}
}
}
//2017年08月04日20:58:12
int main(){
int T;sf("%d",&T);
while(T--){
sf("%lld",&k);
rep(i,0,3){
sf("%lld",&G[i][(i+1)%4]);
G[(i+1)%4][i]=G[i][(i+1)%4];
}
m=2*min(G[1][0],G[1][2]);
ans=((k-1)/m+1)*m;//不小于等于的经典做法
spfa(1);
pf("%lld\n",ans);
}
}