CCF-CSP-201712-4 行车路线

问题描述:

试题编号: 201712-4
试题名称: 行车路线
时间限制: 1.0s
内存限制: 256.0MB
问题描述:

问题描述

  小明和小芳出去乡村玩,小明负责开车,小芳来导航。
  小芳将可能的道路分为大道和小道。大道比较好走,每走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。
  现在小芳拿到了地图,请帮助她规划一个开车的路线,使得按这个路线开车小明的疲劳度最小。

输入格式

  输入的第一行包含两个整数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,疲劳度为52=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 ≤ 105,1 ≤ ab ≤ nt是0或1,c ≤ 105。保证答案不超过106。

一看到这道题就想到用深度优先搜索,毕竟深搜思维简单,数据类型也没考虑。然后不出意外的超时了,30分。╮(╯﹏╰)╭

#include<iostream>
#include<cstring>
#include<algorithm>
#include<iomanip>
#include<cmath>
#include<vector>
using namespace std;
struct node
{
	int d,c,t;
};
bool visited[510];
int minfati;//最小疲劳度 
int totalfati;//当前正在计算的疲劳度 
int totalLen;
int s=0; 
int n;
vector< vector<node> > G(510);
void dfs(int i)
{
	if(i==n) 
	{
		minfati=min(minfati,totalfati);//更新最小疲劳度
		return ; 
	}
	for(int j=0;j<G[i].size();j++)
	{
		node r=G[i][j];
		if(!visited[r.d])
		{   
		    int to0=totalfati,s0=s;
			if(r.t==1) 
			{
				s+=r.c;//小路连续
				if(r.d==n) totalfati+=s*s;//小路通向终点 
			}
			else //大路,连续的小路终结 
			{
				totalfati+=s*s;
				totalfati+=r.c ;
				s=0;
			}
			visited[r.d]=1;
			dfs(r.d);
			visited[r.d]=0;
			totalfati=to0;
			s=s0;
		}		
	}
	
}
int main()
{
    int m;
    cin>>n>>m;
    memset(visited,0,sizeof(visited));
	for(int i=0;i<m;i++)
	{
	    int a,b;
		node r1,r2;
		cin>>r1.t>>a>>b>>r1.c;
		r2.t=r1.t;r2.c=r1.c;r1.d=a;r2.d=b;
		G[a].push_back(r2);
		G[b].push_back(r1);
	} 
	minfati=1<<30;
	totalLen=0;
	totalfati=0;
	visited[1]=1;
	dfs(1);
	cout<<minfati<<endl;
}

然后就想到最优性剪枝,剪了一下提交还是30分(代码就没贴了)。还能怎么办,剪枝又不会剪,每次都超时真的烦(T▽T)

后来查到可以用Dijkstra算法求解。

Dijkstra算法如下:

(1)初始化:D(1)=0,若结点1与结点i有边直接相连,则D(i)等于边权w(1,i),否则D(i)=∞,S={1}。

(2)若﹁S=∅则结束,否则在﹁S中寻找D值最小的点i,S=S+{i},进行下一步。

(3)在﹁S中寻找i的后代j,若d(i)+w(i,j)<d(j) 则置d(j)=d(i)+w(i,j),回到第(2)步。

用来求某点到其他各点的最短距离。放在这一题求最小疲劳度岂不是有异曲同工之妙?就是边权的求法需要分情况讨论。再用一个数组记录到某结点的连续小路长。

AC代码:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<iomanip>
#include<cmath>
#include<vector>
using namespace std;
const int maxn=510;
const long long INF=1<<30; 
struct node
{
	int v,t;
	long long c;
};
int n,m;
vector< vector<node> > G(maxn);//用邻接表来表示图 
bool visited[maxn];
long long d[maxn];
long long con_trail[maxn];//记录到某结点的连续小路长
long long pow_2(long long a)
{
	return a*a;
}
void Dijkstra(int i)
{
	fill(d,d+maxn,INF);//将d[i]初始化为“无穷大”,fill函数在<algorithm>头文件里
	d[i]=0;
	for(int j=0;j<n;j++)
	{
		int u=-1;
		long long MIN=INF;
		for(int k=0;k<n;k++)
		{
			if(!visited[k]&&d[k]<MIN)//在非S中寻找权值最小的点 
			{
				u=k;
				MIN=d[k];
			}
		}
		if(u==-1) return;//非S=空集 
		visited[u]=true;
		for(int p=0;p<G[u].size();p++)
		{
			node r=G[u][p];
			if(visited[r.v]) continue;
			if(r.t ==1)//小路 
			{
				//d[u]+w(u,v)<d[v]
				if(d[u]+pow_2(con_trail[u]+r.c)-pow_2(con_trail[u])<d[r.v])
				{
					d[r.v]=d[u]+pow_2(con_trail[u]+r.c)-pow_2(con_trail[u]);
					con_trail[r.v]=con_trail[u]+r.c;
				}
			}
			else //大路
			{
			    if(d[u]+r.c<d[r.v])
				{
					d[r.v]=d[u]+r.c ;
					con_trail[r.v]=0;//连续的小路被终结 
				}	
			}
		}
	}
}
int main()
{
	int i,a,b;
	cin>>n>>m;
	for(i=0;i<m;i++)
	{
		node n1,n2;
		cin>>n1.t>>a>>b>>n1.c;
		n2.t=n1.t;n2.c=n1.c;n1.v=a;n2.v=b;
		G[a].push_back(n2);
		G[b].push_back(n1);
	} 
	memset(visited,0,sizeof(visited));
	memset(con_trail,0,sizeof(con_trail));
	Dijkstra(1);//从1号路口出发 
	cout<<d[n];
}

猜你喜欢

转载自blog.csdn.net/qq_40889820/article/details/81301345