noip模拟。找位置

转载大神的题解。身为初学者我学了两个晚上才看懂。加了点注释方便大家阅读代码

 

找位置

Problem

问题描述

FJ 想找一个最好的位置来建新农场,这样他每天可以少走些路。FJ 所在的区域,有 N个城镇(1 <= N <= 10,000),城镇之间,有 M(1 <= M <= 50,000)条双向路相连,所有城镇都可以借助一些路相互连接。 
FJ 需要你的帮助来选择最合适建新农场的城镇。K(1 <= K <= 5)个城镇中有超市,FJ 每天都会去这些超市。他计划每天从新农场出发,访问包含超市的 K 个城镇,然后返回新农场。FJ 可以按照任意的顺序访问这些超市。FJ 只会在 N-K 个城镇中,选择一个城镇来建新农场。因为其他城镇的房价,比较低一些。如果他把农场建在最优的位置,而且尽可能明智的选择行走路线。 
请帮 FJ 计算,他每天需要行走的路线长度。

输入

第 1 行:三个空格隔开的整数,N,M 和 K。 
第 2..1+K 行:第 i+1 行包含一个整数,范围 1..N,表示包含第 i 个超市的城镇。每个超市在不同城镇。 
第 2+K..1+K+M 行:每行包含三个空格隔开的整数,i,j(1 <= i,j <= N),和 L(1 <= L <=1000), 表示城镇 i 和城镇 j 之间存在一条长度为 L 的路。

输出

输出一行一个数,表示他把农场建在最优的位置,每天需要行走的最短路线长度。

输入输出样例

relocation.in 
5 6 3 



1 2 1 
1 5 2 
3 2 3 
3 4 5 
4 2 7 
4 5 10 
relocation.out 
12

Solution

做K 遍单源最短路,把K 个超市到每个点的最短路算出来
再用K!的时间预处理dp[i][j],表示先去第i 个超市,最后去第j 个超市的最短
时间
最后枚举所有的非超市点,即新建位置,并枚举先去的超市和最后去的超市,统
计答案

 

#include 
#include 
#include 
#include 
#include 
using namespace std;
const int INF=0x3f3f3f3f; // 0x3f3f3f3f 为极大数=1061109567,是10^9级别的  
int store[10],edge[100010],nxt[100100],val[100100],ecnt,adj[100100],go[100100],dis[7][100100],que[4000000],len,linkedfront[7];
int n,m,k,ans=INF;
bool isstore[100100],vis[100100],islink[7];
inline int read()             //快速读入,比cin和scanf都要快的多 
{
	bool ok=0;int res=0;char c;
	while(((c=getchar())<'0'||c>'9')&&c!='-');
	if(c=='-') ok=1; else res=c-48;
	while((c=getchar())>='0'&&c<='9') res=(res<<3)+(res<<1)+(c-48);  //左移n为表示扩大2的n次方被   res*8+res*2=res*(8+2)=res*10 
	return ok == 1 ? ~res+1:res ;//  ~表示按位取反   ~n+1把n变成负数 
}
void addedge(int u,int v,int w)
{
	nxt[++ecnt]=adj[u];adj[u]=ecnt;go[ecnt]=v;val[ecnt]=w;   
}
void spfa(int st,int r)
{
	que[len=1]=st;
	memset(dis[r],INF,sizeof(dis[r])); dis[r][st]=0;
	for(int i=1;i<=len;i++)
	{ 
		int u=que[i];vis[u]=0;                          //que记录 最短路径起点  当队列来使用 
		for(int e=adj[u],v;e;e=nxt[e])                //adj记录某城镇的边 go记录此边的终点 nex记录此边的下一边 
		{
			if(dis[r][u]+val[e]ans) return;
	if(linked>k) 
	{
		ans=min(ans,now+dis[linkedfront[linked-1]][st]);
		return;
	}
	for(int i=1;i<=k;i++)
	{
		if(!islink[i])
		{
			islink[i]=1;linkedfront[linked]=i;
			dfs(linked+1,now+ (linked==1 ?dis[i][st] : dis[i][store[linkedfront[linked-1]]]),st);
			islink[i]=0;
		}
	}
	
}

int main()
{
//	 freopen("in.txt","r",stdin);
//	 freopen("out.txt","w",stdout);
	n=read();m=read();k=read();               //有n个城镇 m条双向边 k个商店 
	for(int i=1;i<=k;i++)  store[i]=read(),isstore[store[i]]=1;
	int x,y,w;
	for(int i=1;i<=m;i++)
	{
		x=read();y=read();w=read();
		addedge(x,y,w);
		addedge(y,x,w);
	}
	
	for(int i=1;i<=k;i++)spfa(store[i],i);  //遍历含超市的城镇
	
	for(int i=1;i<=n;i++) if(!isstore[i]) dfs(1,0,i);
	printf("%d\n",ans);
//	fclose(stdin);
//	fclose(stdout);
}

 

你可能感兴趣的:(noip模拟。找位置)