CCF201712-4行车路线

题目

问题描述

  小明和小芳出去乡村玩,小明负责开车,小芳来导航。
  小芳将可能的道路分为大道和小道。大道比较好走,每走1公里小明会增加1的疲劳度。小道不好走,如果连续走小道,小明的疲劳值会快速增加,连续走s公里小明会增加s2的疲劳度。
  例如:有5个路口,1号路口到2号路口为小道,2号路口到3号路口为小道,3号路口到4号路口为大道,4号路口到5号路口为小道,相邻路口之间的距离都是2公里。如果小明从1号路口到5号路口,则总疲劳值为(2+2)^2+2+2^2=16+2+4=22。
  现在小芳拿到了地图,请帮助她规划一个开车的路线,使得按这个路线开车小明的疲劳度最小。

输入格式

  输入的第一行包含两个整数nm,分别表示路口的数量和道路的数量。路口由1至n编号,小明需要开车从1号路口到n号路口。
  接下来m行描述道路,每行包含四个整数tabc,表示一条类型为t,连接ab两个路口,长度为c公里的双向道路。其中t为0表示大道,t为1表示小道。保证1号路口和n号路口是连通的。

输出格式

  输出一个整数,表示最优路线下小明的疲劳度。

样例输入

6 7
1 1 2 3
1 2 3 2
0 1 3 30
0 3 4 20
0 4 5 30
1 3 5 6
1 5 6 1

样例输出

76

样例说明

  从1走小道到2,再走小道到3,疲劳度为5^2=25;然后从3走大道经过4到达5,疲劳度为20+30=50;最后从5走小道到6,疲劳度为1。总共为76。

数据规模和约定

  对于30%的评测用例,1 ≤ n ≤ 8,1 ≤ m ≤ 10;
  对于另外20%的评测用例,不存在小道;
  对于另外20%的评测用例,所有的小道不相交;
  对于所有评测用例,1 ≤ n ≤ 500,1 ≤ m ≤ 10*5,1 ≤ ab ≤ nt是0或1,c ≤ 10^5。保证答案不超过10^6。

思路

        这题刚开始我用的是dfs暴力搜索,然后运行超时,后来我就先将小路每段相对较小的路径算出来,再利用宽度优先搜索的思想,找出从1到每一点的最优路径。

代码

#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define LL long long

LL mp1[505][505],n,m;
LL mp2[505][505];
LL dist[505][2];

typedef struct node{
    LL u,d;
    bool big;
    bool operator < (const node &y)const{
        return u > y.u;
    }
}node;
node beg,in,out;

void smallpath() {
    for(int k = 1; k <= n; k++) {
    	for(int i = 1; i <= n; i++) {
    		for(int j = 1; j <= n; j++) {
    			if(mp1[i][j] > mp1[i][k]+mp1[k][j]) {
    				mp1[i][j] = mp1[i][k] + mp1[k][j];
				}
			} 
		}	  	
	}   
    for(int i = 1; i <= n; i++) {
    	for(int j = 1; j <= n; j++) {
        	if(mp1[i][j] != INF) {
        		mp1[i][j] *= mp1[i][j];
			}
    	}
	}
    
}

void init(LL x) {
    for(int i = 1; i <= x; i++) {
    	for(int j = 1; j<= x;j++) {
    		mp2[i][j] = mp1[i][j] = INF;
		}
	}   
}


LL fun() {
    priority_queue<node> Q;
    beg.big = 1;
	beg.u = 1;
	beg.d = 0;
    for(int i = 1; i <= n; i++) {
    	if(i == 1) {
    		dist[i][0] = dist[i][1] = 0;
		}
		else{
			dist[i][0] = dist[i][1] = INF;
		}
	}
    Q.push(beg);
    while(!Q.empty()) {
        out = Q.top();
		Q.pop();
        LL u = out.u,d = out.d;
        for(int i = 1; i <= n; i++) {
            if(u == i) {
            	continue;
			}
            if(out.big && mp1[u][i]!=INF) {
                in.big = 0;
				in.u = i;
				in.d = mp1[u][i]+d;
                if(in.d <= dist[i][1]) {
					dist[i][1] = in.d;
					Q.push(in);
				}
            }
            if(mp2[u][i] != INF) {
                in.big = 1;
				in.u = i;
				in.d = mp2[u][i]+d;
                if(in.d <= dist[i][0]) {
					dist[i][0] = in.d;
					Q.push(in);
				}
            }
        }
    }
    return min(dist[n][0],dist[n][1]);
}

int main(){
    LL t,a,b,c;
    cin >> n >> m;
    init(n);
    int ans = 0;
    for(int i = 0; i < m; i++) {
        cin >> t >> a >> b >> c;
        if(t) {
        	mp1[a][b] = mp1[b][a] = min(c,mp1[b][a]);
		}
        else{
        	mp2[a][b] = mp2[b][a] = min(c,mp2[b][a]);
		} 
    }
    smallpath();
    cout << fun() << endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值