题意:
- 环上有4个点,给定相邻点的距离,找一条以2号点起止的路径,使总长度大于K,求路径的最小长度。
规模:
- T(1≤T≤15)
- K,d1,2,d2,3,d3,4,d4,1(1≤K≤10^18,1≤d≤30000)
类型:
- DP
分析:
- 初看题目,应该是一个DP问题,DP[i][j],i为当前节点编号,j为当前已走长度,那么转移方程是很好写的。
- 但是对于这道题,K太大了,状态数太多。考虑优化;
- 取w=min(d_{1,2},d_{2,3}),那么对于每一种方案,均可以通过往返跑w这条边使得距离增加2w。也就是说,如果存在距离为k的方案,那么必然存在距离为k+2w的方案。
- 这个2w其实是跑一圈的最短长度;
- 这个时候考虑K,2w同余系,如果vis[1][i]==false,即任意跑法都不能得到长度(i+2w),舍去
- 需要记录能够i的最小dis;
时间复杂度&&优化:
- O(4*2*d)
代码:
#include <bits/stdc++.h>
using namespace std;
const int MAXN=70000;
const int INF=0x3fff3fff;
typedef long long ll;
using namespace std;
ll K;
int x1,x2,x3,x4;
bool vis[5][MAXN];
ll dis[5][MAXN];
struct node{
int pos;
int dis;
node(int _pos=0,int _dis=0):pos(_pos),dis(_dis){}
};
int edge[4][4];
void init(){
memset(edge,-1,sizeof(edge));
}
ll DP(ll MOD){
memset(vis,false,sizeof(vis));
memset(dis,0,sizeof(dis));
queue<node > q;
while(!q.empty())q.pop();
q.push(node(1,0));
vis[1][0]=true;
while(!q.empty()){
node tmp=q.front();q.pop();
// cout<<tmp.pos<<" "<<dis[tmp.pos][tmp.dis]<<endl;
node next;
for(int i=0;i<4;i++){
if(edge[tmp.pos][i]==-1)continue;
next.pos=i;
next.dis=(tmp.dis+edge[tmp.pos][i])%MOD;
if(!vis[next.pos][next.dis]||dis[next.pos][next.dis]>dis[tmp.pos][tmp.dis]+edge[tmp.pos][i]){
vis[next.pos][next.dis]=true;
dis[next.pos][next.dis]=dis[tmp.pos][tmp.dis]+edge[tmp.pos][i];
//dis[next.pos][next.dis]=(max((ll)0,K-next.dis-1)/MOD +1)*MOD+next.dis;
q.push(next);
}
}
}
ll x,y,ret=-1;
for(int i=0;i<MOD;i++){
if(vis[1][i]==true){
x=dis[1][i];
if(x>=K)y=x;
else {
y=((K-x-1)/MOD+1)*MOD+x;
}
ret==-1?ret=y:ret=min(ret,y);
}
}
return ret;
}
int main(){
int T;
scanf("%d",&T);
while(T--){
init();
scanf("%I64d%d%d%d%d",&K,&edge[0][1],&edge[1][2],&edge[2][3],&edge[3][0]);
edge[1][0]=edge[0][1];
edge[2][1]=edge[1][2];
edge[3][2]=edge[2][3];
edge[0][3]=edge[3][0];
ll MOD=min(2*edge[0][1],2*edge[1][2]);
printf("%I64d\n",DP(MOD));
}
return 0;
}