(一)题目大意:
小芳将可能的道路分为大道和小道。大道比较好走,每走1公里小明会增加1的疲劳度。小道不好走,如果连续走小道,小明的疲劳值会快速增加,连续走s公里小明会增加s2的疲劳度。
例如:有5个路口,1号路口到2号路口为小道,2号路口到3号路口为小道,3号路口到4号路口为大道,4号路口到5号路口为小道,相邻路口之间的距离都是2公里。如果小明从1号路口到5号路口,则总疲劳值为(2+2)2+2+22=16+2+4=22。
现在小芳拿到了地图,请帮助她规划一个开车的路线,使得按这个路线开车小明的疲劳度最小。
接下来m行描述道路,每行包含四个整数t, a, b, c,表示一条类型为t,连接a与b两个路口,长度为c公里的双向道路。其中t为0表示大道,t为1表示小道。保证1号路口和n号路口是连通的。
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
对于另外20%的评测用例,不存在小道;
对于另外20%的评测用例,所有的小道不相交;
对于所有评测用例,1 ≤ n ≤ 500,1 ≤ m ≤ 105,1 ≤ a, b ≤ n,t是0或1,c ≤ 105。保证答案不超过106。
(二)解题思路:
这个题在官网上发出来以后,开始并没有想到什么解决的办法,但是看了数据范围之后发现骗70分还是比较简单的:其中的三个测试点n<=8,那么我们可以直接生成1~n的一个全排列,比如1->2->3->5->7->4->-6>8,表示一条1->8的路径,我们暴力找出所有可能路径的最小值便可以得出结果(路径上要记录连续走过的小路的长度),对于另外四个测试点,都是不存在连续的小路的,那么我们直接将小路的距离平方,接下来就是普通的求两点之间的最短路,这里就可以骗到70分了。
好了,接下来进入正题:这个题比较麻烦的是走小路的时候不是很方便处理,需要记录连续走多条小路时的总长度,后来便想到了先求出所有的连续走小路的情况,然后在求最终路径的时候不让走连续小路即可,具体操作为: 将所有的小路跑一遍floyd,跑完以后把所有的边平方。这样做完以后为什么可以转化为不让其走连续小路呢?仔细想想,如果两个点之间可以走连续小路到达,那么在跑完floyd后,将会有一条直接连接这两个点的路,且长度为这些小路长度和的平方。
做完上一个步骤之后还是求最短路,我们类似Dijkstra的做法,只不过要求图中不能走连续小路即可:对于到达每个的距离保存两个最小值,分别是走小路到达和走大路到达。每个点也保留两个状态,表示前驱路径分别小路和大路,然后某个点由小路到达的最短路径只能由前驱路径是大路的点去更新,说起来有点绕,具体细节参见代码:
(三)具体代码:
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<queue>
#include<vector>
#include<algorithm>
#define INF 0x3f3f3f3f
#define LL long long
using namespace std;
const int maxn=520;
LL mps[maxn][maxn],n,m;
LL mpm[maxn][maxn];
LL dist[maxn][2];
void Deal_with_small(){
for(register int k=1;k<=n;k++)
for(register int i=1;i<=n;i++)
for(register int j=1;j<=n;j++)
if(mps[i][j]>mps[i][k]+mps[k][j])mps[i][j]=mps[i][k]+mps[k][j];
for(register int i=1;i<=n;i++)
for(register int j=1;j<=n;j++){
if(mps[i][j]!=INF)mps[i][j]*=mps[i][j];
}
}
void init(LL nn){
for(register int i=1;i<=nn;i++)
for(register int j=1;j<=nn;j++)
mpm[i][j]=mps[i][j]=INF;
}
struct node{
LL u,d;
bool big;
bool operator < (const node &n1)const{
return u>n1.u;
}
}beg,in,out;
LL Dijkstra(){
priority_queue<node>Q;
beg.big=1;beg.u=1;beg.d=0;
for(register int i=1;i<=n;i++)dist[i][0]=dist[i][1]=(i==1?0:INF);
Q.push(beg);
while(!Q.empty()){
out=Q.top();Q.pop();
LL u=out.u,d=out.d;
for(register int i=1;i<=n;i++){
if(u==i)continue;
if(out.big&&mps[u][i]!=INF){
in.big=0;in.u=i;in.d=mps[u][i]+d;
if(in.d<=dist[i][1]){dist[i][1]=in.d;Q.push(in);}
}
if(mpm[u][i]!=INF){
in.big=1;in.u=i;in.d=mpm[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 x,y,d,t;
scanf("%lld%lld",&n,&m);
init(n);
int ans=0;
for(register int i=0;i<m;i++){
scanf("%lld%lld%lld%lld",&t,&x,&y,&d);
if(t)mps[x][y]=mps[y][x]=min(d,mps[y][x]);
else mpm[x][y]=mpm[y][x]=min(d,mpm[y][x]);
}
Deal_with_small();
cout<<Dijkstra()<<endl;
return 0;
}
显然这个要跑floyd是O(n^3)的,n=500的话,一般情况下1s是比较难跑过的(这里优化一些细节后只跑了500+ms,CCF的测评姬强啊),所以这个做法并不是很优秀(真滴菜啊QWQ),日后再思考更加优秀的做法吧。
下面顺便给出那个骗分的70分代码:
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<vector>
#include<algorithm>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn=520;
int mp[maxn][maxn],n,m;
int dist[maxn],done[maxn];
struct Edge{
bool big;
int Dist;
};
vector<Edge>edges;
int Small_m(int nn){
int arr[12];
int min_ans=INF;
for(int i=0;i<nn;i++)arr[i]=i+1;
do{
if(arr[0]!=1)continue;
int mid_ans=0,lastm=0;
for(int i=0;i<nn;i++){
if(!i)continue;
if(mp[arr[i-1]][arr[i]]==INF)break;
int edg=mp[arr[i-1]][arr[i]];
if(edges[edg].big==0){
mid_ans+=lastm*lastm+edges[edg].Dist;
lastm=0;
if(arr[i]==n){
min_ans=min(min_ans,mid_ans);
break;
}
}
else{
lastm+=edges[edg].Dist;
if(arr[i]==n){
mid_ans+=lastm*lastm;
min_ans=min(min_ans,mid_ans);
break;
}
}
}
}while(next_permutation(arr,arr+nn));
return min_ans;
}
void Dijkstra(){
memset(done,0,sizeof(done));
for(int i=1;i<=n;i++)dist[i]=(i==1?0:INF);
for(int i=1;i<=n;i++){
int x,m=INF;
for(int y=1;y<=n;y++)if(!done[y]&&dist[y]<=m)m=dist[x=y];
done[x]=1;
for(int y=1;y<=n;y++)dist[y]=min(dist[y],dist[x]+mp[x][y]);
}
}
int main(){
Edge E;
int x,y,d,t;
memset(mp,0x3f,sizeof mp);
cin>>n>>m;
int ans=0;
if(n<=10){
for(int i=0;i<m;i++){
cin>>t>>x>>y>>d;
E.big=t;E.Dist=d;
edges.push_back(E);
mp[x][y]=mp[y][x]=i;
ans=Small_m(n);
}
}
else{
for(int i=0;i<m;i++){
cin>>t>>x>>y>>d;
if(t)d*=d;
mp[x][y]=mp[y][x]=d;
}
Dijkstra();
ans=dist[n];
}
cout<<ans<<endl;
return 0;
}