201712-4 行车路线
思路
拆点这种算法还是第一次见,不过能拿一点分还是可以接受吧。
主要就是说把点分为d[x][y], x表示某个点,y表示最后一次连续的小路长度。
这样对于一个d[x][y],如果它邻接的点对应的边是大路,那么就是d[u][0] = d[x][y] + w[i],否则就是d[u][y + w[i]] = d[x][y] - y * y + (y + w[i]) * (y + w[i])
因为边权都为正,所以可以用迪杰斯特拉算法(SPFA算法会超时)
代码
#include
#include
#include
#include
using namespace std;
const int N = 515;
const int M = 200010;
const int INF = 1e6;
int h[N], e[M], ne[M], w[M], f[M], idx;
bool st[N][1010];
int d[N][1010];
int n,m;
typedef struct node{
int x,y,v;//x表示哪一个点,y表示它最后一次经过的连续的小路长度,v表示它距离源点的距离
bool operator < (const node& a) const {
return v > a.v;
}
}node;
void add(int t, int u, int v, int c){
f[idx] = t, e[idx] = v, w[idx] = c, ne[idx] = h[u], h[u] = idx++;
}
void dijkstra(){
memset(d, 0x3f, sizeof(d));
priority_queue q;
d[1][0] = 0;
q.push({1, 0, 0});
while(!q.empty()){
auto t = q.top();
q.pop();
if(st[t.x][t.y])continue;
st[t.x][t.y] = true;
for(int i = h[t.x]; i != -1;i = ne[i]){
int x = e[i], y = t.y;
if(f[i]){
y += w[i];
if(y <= 1000){
if(d[x][y] > t.v - t.y * t.y + y * y){
d[x][y] = t.v - t.y * t.y + y * y;
if(d[x][y] <= INF){
q.push({x, y, d[x][y]});
}
}
}
}else{
if(d[x][0] > t.v + w[i]){
d[x][0] = t.v + w[i];
if(d[x][0] <= INF){
q.push({x, 0, d[x][0]});
}
}
}
}
}
}
int main(){
cin>>n>>m;
memset(h, -1, sizeof(h));
while(m--){
int t,u,v,w;
scanf("%d%d%d%d", &t, &u, &v, &w);
add(t, u, v, w);
add(t, v, u, w);
}
dijkstra();
int res = INF;
for(int i = 0;i<=1000;i++)res = min(res, d[n][i]);
cout<