kuangbin最短路径专题题解

https://cn.vjudge.net/problem/POJ-1062

枚举区间,以国王的等级为起点枚举,如图(有点丑)

然后第一次枚举的时候1,2,3,4等级的可以走,第二次,2,3,4,5,第三次3,4,5,6,然后跑最短路

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<map>
//#include<regex>
#include<cstdio>
using namespace std;
const int maxn=300;
int dis[maxn];
bool vis[maxn],v[maxn];
int ji[maxn],m,n,w[maxn];
bool can[maxn];
const long long inf=0x3f3f3f3f;
struct node
{
	int to,cap;
	node(){}
	node(int to,int cap):to(to),cap(cap){}
	bool operator < (const node &a)const
	{
		return cap>a.cap;
	}
};
vector<node>g[maxn];
void add(int u,int v,int cap)
{
	g[u].push_back(node(v,cap));
//	g[v].push_back(node(u,cap));
}
int dijkstra(int s)
{   int ans=0x3f3f3f3f;
    memset(vis,false,sizeof(vis));
	priority_queue<node>q;
	q.push(node(s,0));
	memset(dis,0x3f3f3f3f,sizeof(dis));
	dis[s]=0;
	while(!q.empty())
	{
		node s=q.top();q.pop();
		//if(dis[s.to]>s.cap)
		if(vis[s.to]||!can[s.to])continue;
		vis[s.to]=true;
		//ans+=s.cap;
		for(int i=0;i<g[s.to].size();i++)
		{
			if(!vis[g[s.to][i].to]&&dis[g[s.to][i].to]>dis[s.to]+g[s.to][i].cap)
			{
				dis[g[s.to][i].to]=dis[s.to]+g[s.to][i].cap;
				q.push(g[s.to][i]);
			}
		}
	}
	for(int i=1;i<=n;i++)
	{   if(can[i])
		ans=min(ans,w[i]+dis[i]);
	}
	return ans;
}
int main()
{   int ans=0x3f3f3f3f;
	scanf("%d %d",&m,&n);
	for(int i=1;i<=n;i++)
	{   //if(i!=1)add(0,i,0);
		int cos,le,e;
		scanf("%d %d %d",&cos,&le,&e);
		ji[i]=le;//level表示等级,中英混用
		w[i]=cos;//cost
		for(int j=0;j<e;j++)
		{
			int v,co;
			scanf("%d %d",&v,&co);
			add(i,v,co);
		}
	}
	for(int i=0;i<=m;i++)//此处枚举区间
	{   memset(can,false,sizeof(can));//这个一定要有
	    //can[0]=true;
		for(int j=1;j<=n;j++)
		{
			if(ji[j]>=ji[1]-m+i&&ji[j]<=ji[1]+i)
			{
				can[j]=true;
			}
		}
		ans=min(ans,dijkstra(1));
	}
	cout<<ans<<endl;
} 

 

https://cn.vjudge.net/problem/POJ-2253

题意:找从编号为1-2之间的所有路径中,每条路径上最大值最小化。这是一个完全图。

n=200。不建议用前向星和链表存图(边很多),直接开个200*200数组就解决了。

题解:有一些动态规划的思想g[i][j]表示从i-j的最大值的最小化,然后遍历他的所有编,在更新下一个状态。

Floyd解法

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<map>
//#include<regex>
#include<cstdio>
using namespace std;
#define ll long long
const int maxn=1e5+10;
const int inf=0x3f3f3f3f;
double g[3000][300];
int n,cnt;
struct Point
{
	int x,y;
	Point(){}
	Point(int x,int y):x(x),y(y){}
}p[maxn];
double dis(Point a,Point b)
{
	return sqrt(double((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)));
}
void floyd()
{
	for(int k=0;k<n;k++)//这个思想就是,从K点转移
                            //从i-j的最小值。所以先遍历k
       for(int i=0;i<n;i++)
          for(int j=0;j<n;j++)
          	g[j][i]=g[i][j]=min(g[i][j],max(g[i][k],g[k][j]));
}
int main()
{
	while(scanf("%d",&n)!=EOF&&n){
		memset(g,inf,sizeof(g));
		for(int i=0;i<n;i++)
		{
			scanf("%d %d",&p[i].x,&p[i].y);
		}
		for(int i=0;i<n;i++)
		{
			for(int j=i+1;j<n;j++)
			{
				g[i][j]=dis(p[i],p[j]);//完全图建边
			}
		}
		floyd();
	//printf("Scenario #%d\nFrog Distance = %.3lf\n\n",q++,map[1][2]);
	printf("Scenario #%d\nFrog Distance = %.3lf\n\n",++cnt,g[0][1]);
	}
}

dijkstra法

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<map>
//#include<regex>
#include<cstdio>
using namespace std;
#define ll long long
const int maxn=1e5+10;
const int inf=0x3f3f3f3f;
double g[300][300];
int n,cnt;
bool vis[300];
double dist[300];
struct Point
{
	int x,y;
	Point(){}
	Point(int x,int y):x(x),y(y){}
}p[maxn];
double dis(Point a,Point b)
{
	return sqrt(double((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)));
}
void dij(int s)//其实想法是差不多的,这个也不算dijkstra只是一种思想
{
	memset(vis,false,sizeof(vis));
	for(int i=0;i<300;i++)dist[i]=0x3f3f3f3f;
	dist[0]=0;
	for(int i=0;i<n;i++)
	{   int minn=0x3f3f3f3f,k;
		for(int j=0;j<n;j++)//这个循环每次寻找最短的那条边 
		{   
			if(!vis[j]&&dist[j]<minn)
			{
				minn=dist[j];
				k=j;
			}
		}
		vis[k]=true;//这就是最短的那条边 
		for(int j=0;j<n;j++) 更新状态
		dist[j]=min(dist[j],max(dist[k],g[k][j]));
    }
}
int main()
{
	while(scanf("%d",&n)!=EOF&&n){
		//memset(g,inf,sizeof(g));
		for(int i=0;i<n;i++)
		{
			scanf("%d %d",&p[i].x,&p[i].y);
		}
		for(int i=0;i<n;i++)
		{
			for(int j=i+1;j<n;j++)
			{
				g[j][i]=g[i][j]=dis(p[i],p[j]);
			}
		}
		dij(0);
	//printf("Scenario #%d\nFrog Distance = %.3lf\n\n",q++,map[1][2]);
	printf("Scenario #%d\nFrog Distance = %.3lf\n\n",++cnt,dist[1]);
	}
}

建边的时候注意双向边!!!

https://cn.vjudge.net/problem/POJ-1860

典型的BF问题,题目的意思是判是否有正环,那就判断能否已知松弛,并且是反着来松弛

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<map>
//#include<regex>
#include<cstdio>
using namespace std;
const int maxn=300;
int cnt;
double dis[maxn];
	int N,M,S;
	double V;
struct node
{
	int u,v;
	double re,ra;
}g[maxn];
void add(int u,int v,double re,double ra)
{
	g[cnt].u=u;
	g[cnt].v=v;
	g[cnt].ra=ra;
	g[cnt].re=re;
	cnt++;
}
bool BF()
{
for(int i=0;i<N;i++)dis[i]=0;
dis[S]=V;
   for(int j=1;j<N;j++)//BF思想,进行N-1次的松弛操作
  {  bool ok=false;
     for(int i=0;i<cnt;i++)//对源点,经过第j条边时的松弛
                           //也就是不断的更新。
	 {
	 	if(dis[g[i].v]<(dis[g[i].u]-g[i].ra)*g[i].re)
	 	dis[g[i].v]=(dis[g[i].u]-g[i].ra)*g[i].re,ok=true;
	 }
	 if(!ok)return 0;//若没松弛完便无法松弛了,那么也就不存在正环。
  }
  for(int j=0;j<cnt;j++)//判断是否存在正环
  {
  	if(dis[g[j].v]<(dis[g[j].u]-g[j].ra)*g[j].re)
  	return 1;
  }
  return 0;
}
int main()
{
	int u,v;
	double re,ra,ve,va;
	cin>>N>>M>>S>>V;
	for(int i=0;i<M;i++)
	{
		cin>>u>>v>>re>>ra>>ve>>va;
		add(u,v,re,ra);
		add(v,u,ve,va);
	}
	if(BF())cout<<"YES"<<endl;
	else cout<<"NO"<<endl;
	
}

BF判断负环,跟上面的操作类似,就是反过来

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<map>
//#include<regex>
#include<cstdio>
using namespace std;
const int maxn=6000;
int total;
int n,m,w;
int dis[maxn];
struct node
{
	int u,v,cap;
	node(){}
	node(int u,int v,int cap):u(u),v(v),cap(cap){}
}g[maxn];
void add(int u,int v,int cap)
{
	g[total++]=node(u,v,cap);
}
bool BF(int s)
{
	for(int i=0;i<n;i++)
	dis[i]=0x3f3f3f3f;
	dis[s]=0;
	for(int i=0;i<n;i++)
	{   bool ok=false;
		for(int j=0;j<total;j++)//松弛
		{
			if(dis[g[j].v]>dis[g[j].u]+g[j].cap)
			dis[g[j].v]=dis[g[j].u]+g[j].cap,ok=true;
		}
		if(!ok)return false;//已经不能松弛了,说明没有跑完N-1就已经到达最好的值,所以不存在环
	}
	for(int i=0;i<total;i++)//跑完后,判断是否有负环
	if(dis[g[i].v]>dis[g[i].u]+g[i].cap)return true;
	return false;
}
int main()
{
	int t;
	cin>>t;
	while(t--)
	{   total=0;
		cin>>n>>m>>w;
		for(int i=0;i<maxn;i++)
		g[i].u=g[i].v=g[i].cap=0;
		int u,v,ca;
		for(int i=0;i<m;i++)
		{
			cin>>u>>v>>ca;
			add(u,v,ca);
			add(v,u,ca);
		}
		for(int i=0;i<w;i++)
		{
			cin>>u>>v>>ca;
			add(u,v,-ca);
		}
		bool ok=false;
		if(BF(1))
		printf("YES\n");
		else printf("NO\n");
	}
}

https://cn.vjudge.net/problem/POJ-1502

一道最短路的裸题吧···比较难的地方可能就是只给了一个三角矩阵了,自己还原后找规律就知道了;

样例的原矩阵是这个

0     50 30 100 10 
50    0   5   20   x
30    5   0   50   x
100 20 50   0   10
10    x   x    10   0

用floyd跑一编,然后g[0][i]的最大值就是答案。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<map>
//#include<regex>
#include<cstdio>
using namespace std;
const int maxn=200;
int n;
#define inf 0x7ffffff
int g[maxn][maxn];
int mp[maxn][maxn];
int get(string s)
{
	int ans=0;
	for(int i=0;i<s.size();i++)
	{
		ans=ans*10+(s[i]-'0');
	}
	return ans;
}
void floyd()
{
	for(int k=0;k<n;k++)
	{
		for(int i=0;i<n;i++)
		{
			for(int j=0;j<n;j++)
			{
				g[i][j]=min(g[i][j],g[i][k]+g[k][j]);
			}
		}
	}
}
int main()
{
	memset(g,0x3f3f3f3f,sizeof(g));
	cin>>n;
	for(int i=0;i<n;i++)
	g[i][i]=0;
	for(int i=0;i<n-1;i++)
	{
		for(int j=0;j<i+1;j++)
		{
			string s;
			cin>>s;
			if(s=="x")continue;
			else g[i+1][j]=g[j][i+1]=get(s);//记得双向边
		}
	}
	floyd();
	int res=0;
	for(int i=0;i<n-1;i++)
	{
		res=max(g[0][i],res);
	}
	cout<<res<<endl;
}

https://cn.vjudge.net/problem/POJ-3660

思路:首先考虑怎么样确定关系?即这个点与别的n-1个点都有联系,那么就floyd跑一遍最短路,然后每个点特判就好了。

还有一个方法,就是记录入度与出度只要 入度+出度=n-1就可以了。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<map>
//#include<regex>
#include<cstdio>
using namespace std;
#define ll long long 
const int maxn=300;
int g[maxn][maxn],n,m;
void floyd()
{
	for(int k=0;k<n;k++)
	{
		for(int i=0;i<n;i++)
		{
			for(int j=0;j<n;j++)
			{
				g[i][j]=min(g[i][j],g[i][k]+g[k][j]);
			 } 
		}
	}
}
int main()
{
	cin>>n>>m;
	memset(g,0x3f3f3f3f,sizeof(g));
	for(int i=0;i<n;i++)
	g[i][i]=0;
	for(int i=0;i<m;i++)
	{
		int u,v;
		cin>>u>>v;
		g[--u][--v]=1;
	}
	floyd();
	int ans=0;
	for(int i=0;i<n;i++)
	{
		bool ok=true;
		for(int j=0;j<n;j++)
		{    //cout<<g[i][j]<<endl;
			if(g[i][j]>=50000&&g[j][i]>40000)
			{
				ok=false;
				break;
			}
		}
		if(ok)ans++;
	}
	cout<<ans<<endl;
 } 

https://cn.vjudge.net/problem/POJ-3159

这题很经典的题型了,先正着跑一边dij的堆优化然后反着跑一边,加起来就是答案

用vector建图会被卡常超时,这里用邻接表,也挺好用的。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<string>
//#include<regex>
#include<cstdio>
using namespace std;
#define ll long long 
#define maxn 1000100
#define inf 1LL<<60
struct node
{
	int to,cap,next;
	node(){}
	node(int to,int cap):to(to),cap(cap){}
	bool operator < (const node &a)const
	{
		return  cap>a.cap;
	}
}g1[maxn*10],g2[maxn*10];
int head1[maxn],head2[maxn];
long long dis[maxn];
bool vis[maxn];
int n,m,cnt;
void add(node *mp,int *head,int u,int v,int cap)
{
	mp[cnt].to=v;
	mp[cnt].cap=cap;
	mp[cnt].next=head[u];
	head[u]=cnt;
}
long long dijkstra(node *g,int *head,int s)
{   long long res=0;
	memset(vis,false,sizeof(vis));
	for(int i=0;i<=n;i++)dis[i]=inf;
	dis[s]=0;
	priority_queue<node>q;
	q.push(node(1,0));
	while(!q.empty())
	{
		node s=q.top();q.pop();
		if(vis[s.to])continue;
		vis[s.to]=true;
		for(int i=head[s.to];i!=-1;i=g[i].next)
		{   //cout<<i<<endl;
			if(!vis[g[i].to]&&dis[g[i].to]>dis[s.to]+g[i].cap)
			{
			dis[g[i].to]=dis[s.to]+g[i].cap;
			 q.push(g[i]);
			}
		}
	}
	for(int i=1;i<=n;i++)
	{
		res+=dis[i];
	}
	return res;
}
int main()
{
	int t;
    scanf("%d",&t);
    while(t--)
    {
	scanf("%d %d",&n,&m);
	memset(head1,-1,sizeof(head1));
	memset(head2,-1,sizeof(head2));
	for(int i=0;i<m;i++)
	{
		int u,v,ca;
		scanf("%d %d %d",&u,&v,&ca);
	//	cout<<u<<" "<<v<<" "<<ca<<endl;
		add(g1,head1,u,v,ca);//存正图
		add(g2,head2,v,u,ca);//存反图
		cnt++;
		//g1[v].push_back(node(u,ca));
		//cout<<g[u].size()<<endl;
	}
	//for(int i=0;i<n;i++)cout<<g[i].size()<<endl;
	long long ans=dijkstra(g1,head1,1)+dijkstra(g2,head2,1);
	printf("%lld\n",ans);
   }
}

https://cn.vjudge.net/problem/POJ-1511

两题放在一起因为题型类似,这题以1为起点跑一遍最短路,再以n为起点跑一遍,取最小的最是答案了

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<string>
//#include<regex>
#include<cstdio>
using namespace std;
#define ll long long 
#define maxn 1000100
#define inf 1LL<<60
struct node
{
	int to,cap,next;
	node(){}
	node(int to,int cap):to(to),cap(cap){}
	bool operator < (const node &a)const
	{
		return  cap>a.cap;
	}
}g1[maxn*10],g2[maxn*10];
int head1[maxn],head2[maxn];
long long dis[maxn];
bool vis[maxn];
int n,m,cnt;
void add(node *mp,int *head,int u,int v,int cap)
{
	mp[cnt].to=v;
	mp[cnt].cap=cap;
	mp[cnt].next=head[u];
	head[u]=cnt;
}
void dijkstra(node *g,int *head,int s)
{
	memset(vis,false,sizeof(vis));
	for(int i=0;i<=n;i++)dis[i]=inf;
	dis[s]=0;
	priority_queue<node>q;
	q.push(node(1,0));
	while(!q.empty())
	{
		node s=q.top();q.pop();
		if(vis[s.to])continue;
		vis[s.to]=true;
		for(int i=head[s.to];i!=-1;i=g[i].next)
		{   //cout<<i<<endl;
			if(!vis[g[i].to]&&dis[g[i].to]>dis[s.to]+g[i].cap)
			{
			dis[g[i].to]=dis[s.to]+g[i].cap;
			 q.push(g[i]);
			}
		}
	}

}
int main()
{
	scanf("%d %d",&n,&m);
	memset(head1,-1,sizeof(head1));
	for(int i=0;i<m;i++)
	{
		int u,v,ca;
		scanf("%d %d %d",&u,&v,&ca);
		add(g1,head1,u,v,ca);
		cnt++;
	}
	
	long long int ans=inf;
	dijkstra(g1,head1,1);
	ans=min(dis[n],ans);
	dijkstra(g1,head1,n);
	ans=min(dis[1],ans);
	printf("%lld\n",ans);
}

https://cn.vjudge.net/problem/POJ-1062

题解:枚举区间跑dijkstra,一开始想用floyd然后逐项判断的,发现行不通,无法根据等级判断。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<map>
//#include<regex>
#include<cstdio>
using namespace std;
const int maxn=300;
int dis[maxn];
bool vis[maxn],v[maxn];
int ji[maxn],m,n,w[maxn];
bool can[maxn];
const long long inf=0x3f3f3f3f;
struct node
{
	int to,cap;
	node(){}
	node(int to,int cap):to(to),cap(cap){}
	bool operator < (const node &a)const
	{
		return cap>a.cap;
	}
};
vector<node>g[maxn];
void add(int u,int v,int cap)
{
	g[u].push_back(node(v,cap));
//	g[v].push_back(node(u,cap));
}
int dijkstra(int s)
{   int ans=0x3f3f3f3f;
    memset(vis,false,sizeof(vis));
	priority_queue<node>q;
	q.push(node(s,0));
	memset(dis,0x3f3f3f3f,sizeof(dis));
	dis[s]=0;
	while(!q.empty())
	{
		node s=q.top();q.pop();
		//if(dis[s.to]>s.cap)
		if(vis[s.to]||!can[s.to])continue;
		vis[s.to]=true;
		//ans+=s.cap;
		for(int i=0;i<g[s.to].size();i++)
		{
			if(!vis[g[s.to][i].to]&&dis[g[s.to][i].to]>dis[s.to]+g[s.to][i].cap)
			{
				dis[g[s.to][i].to]=dis[s.to]+g[s.to][i].cap;
				q.push(g[s.to][i]);
			}
		}
	}
	for(int i=1;i<=n;i++)
	{   if(can[i])
		ans=min(ans,w[i]+dis[i]);
	}
	return ans;
}
int main()
{   int ans=0x3f3f3f3f;
	scanf("%d %d",&m,&n);
	for(int i=1;i<=n;i++)
	{   //if(i!=1)add(0,i,0);
		int cos,le,e;
		scanf("%d %d %d",&cos,&le,&e);
		ji[i]=le;
		w[i]=cos;
		for(int j=0;j<e;j++)
		{
			int v,co;
			scanf("%d %d",&v,&co);
			add(i,v,co);
		}
	}
	for(int i=0;i<=m;i++)//枚举区间
	{   memset(can,false,sizeof(can));
	    //can[0]=true;
		for(int j=1;j<=n;j++)
		{
			if(ji[j]>=ji[1]-m+i&&ji[j]<=ji[1]+i)
			{
				can[j]=true;
			}
		}
		ans=min(ans,dijkstra(1));
	}
	cout<<ans<<endl;
} 

https://cn.vjudge.net/problem/POJ-2502

这题主要考建图把,一开始建崩了,还是不知道怎么错的,后来修改了。唉。。。

每个地铁站以及学校和家作为点,边值就是两个点通过任意方式到达的最短时间,然后跑一遍最短路,可能会有重边,注意四舍五入的判断

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<string>
//#include<regex>
#include<cstdio>
using namespace std;
#define inf 0x3f3f3f3f
int k;
struct Point
{
	int x,y;
	Point(){}
	Point(int x,int y):x(x),y(y){}
}sta[320];
bool link[320][320];
double mp[320][320];
double dis[320];
bool vis[320];
double ans;
struct node
{
	int to;
	double cap;
	node(){}
	node(int to,double cap):to(to),cap(cap){}
	bool operator < (const node &a)const
	{
		return cap>a.cap;
	}
};
double dis1(Point x,Point y)
{
	return (double)sqrt((double)(x.x-y.x)*(x.x-y.x)+(double)(x.y-y.y)*(x.y-y.y));
}
void dijkstra()
{
	memset(vis,false,sizeof(vis));
	for(int i=0;i<k;i++)dis[i]=inf;
	dis[0]=0;
	priority_queue<node>q;
	q.push(node(0,0));
	while(!q.empty())
	{
		node s=q.top();q.pop();
		if(vis[s.to])continue;
		vis[s.to]=true;
		for(int i=0;i<k;i++)
		{
			if(!vis[i]&&dis[i]>dis[s.to]+mp[s.to][i])
			{
				dis[i]=dis[s.to]+mp[s.to][i];
				q.push(node(i,dis[i]));
			}
		}
	}
	ans=dis[1];
	return ;
}
int main()
{
	int sx,sy,ex,ey;
	memset(link,false,sizeof(link));
	for(int i=0;i<320;i++)
	for(int j=0;j<320;j++)
	mp[i][j]=inf;
	scanf("%d %d %d %d",&sx,&sy,&ex,&ey);
	int cnt=1;
	int bx,by;
	sta[k++]=Point(sx,sy);
	sta[k++]=Point(ex,ey);
	int ahead=k; 
	while(scanf("%d %d",&bx,&by)!=EOF)
	{   
	    sta[k++]=Point(bx,by);
		while(scanf("%d %d",&bx,&by)!=EOF&&(bx!=-1&&by!=-1))
		{    sta[k]=Point(bx,by);
		    mp[k][k-1]=mp[k-1][k]=min(mp[k][k-1],3.0*dis1(sta[k],sta[k-1])/2000.0);
		    k++;
		}
	}
	for(int i=0;i<k;i++)
	{   mp[i][i]=0;
		for(int j=0;j<k;j++)
		{
			mp[i][j]=mp[j][i]=min(mp[i][j],3.0*dis1(sta[i],sta[j])/500.0);
		}
	}
	dijkstra();
	int tans=(int)(ans+0.5);
	printf("%d\n",tans);
	return 0;
} 

https://cn.vjudge.net/problem/LightOJ-1074

很经典的问题,跑一遍SPFA就好了,判断负环

//写的时候发现自己的代码很不规范,最后因为if后面加了个;号·找了半天
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<string>
//#include<regex>
#include<cstdio>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=220;
const int maxm=maxn*maxn;
int cnt,dis[maxn],w[maxn];
bool vis[maxn],cir[maxn];
int now[maxn];
int n,m;
struct node
{
	int v,cap,next;
}edge[maxm];
int head[maxm];
void add(int u,int v,int cap)
{
	edge[cnt].v=v;
	edge[cnt].cap=cap;
	edge[cnt].next=head[u];
	head[u]=cnt;
	//cnt++;
}
void dfs(int u)
{
	cir[u]=true;
	for(int i=head[u];i!=-1;i=edge[i].next)
	{
		if(!cir[edge[i].v])
		dfs(edge[i].v);
	} 
}

void SPFA()
{
	for(int i=0;i<=n;i++)
	{
		dis[i]=inf;
	}
	memset(vis,false,sizeof(vis));
	memset(now,0,sizeof(now));
	memset(cir,false,sizeof(cir));
	stack<int>q;
	while(!q.empty())q.pop();
	q.push(1);
	dis[1]=0;
	vis[1]=1;
	now[1]=1;
	while(!q.empty())
	{
		int s=q.top();q.pop();
		//s=head[s];
		vis[s]=false;
	   for(int i=head[s];i!=-1;i=edge[i].next)
		{   //int to=head[i];
		    int v=edge[i].v;
		    int cap=edge[i].cap;
			if(dis[v]>(dis[s]+cap)&&!cir[s])
			{
				dis[v]=dis[s]+cap;
				if(!vis[v])
				{
					vis[v]=true;
					now[v]++;
					if(now[v]>=n&&!cir[v])
					dfs(v);
					else
					q.push(v);
				}
			}
		}
	}
	return ;
}
int main()
{
	int t;
	scanf("%d",&t);
	for(int cas=1;cas<=t;cas++)
	{   cnt=0;
	    //memset(mp,0x3f3f3f3f,sizeof(mp));
	    memset(head,-1,sizeof(head));
		scanf("%d",&n);
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&w[i]);
		}
	    scanf("%d",&m);
	    for(int i=0;i<m;i++)
	    {
	    	int u,v;
	    	scanf("%d %d",&u,&v);
	    	add(u,v,(w[v]-w[u])*(w[v]-w[u])*(w[v]-w[u]));
	    	cnt++; 
		}
		SPFA();
		int num;
		scanf("%d",&num);
		printf("Case %d:\n",cas);
		for(int i=0;i<num;i++)
		{
			int q;
			scanf("%d",&q);
			if(dis[q]<3||dis[q]==inf||cir[q])
			{
				printf("?\n");
			}
			else
			{
				printf("%d\n",dis[q]);
			}
		}
	}
}

https://cn.vjudge.net/problem/HDU-4725

建图思想比较好···一开始建崩了,看了题解,发现真的挺好的想法,就是每一层增加两个点,一个出口一个入门,也就是把每一层并成一个集合,就可以当作一个点。入口到当前层的每个点距离为0,出口连接别的上下层的入口,距离为3,然后跑dij就好了

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<string>
//#include<regex>
#include<cstdio>
using namespace std;
#define ll long long 
#define maxn 312345 
#define inf 0x3f3f3f3f
bool vis[maxn];
int dis[maxn],n,m,cnt;
//bool vist[maxn];
struct node
{
	int v,cap,next;
	node(){}
	node(int v,int cap):v(v),cap(cap){}
	bool operator < (const node &a)const
	{
		return cap>a.cap;
	}
}edge[maxn*3];
int head[maxn*3];
//vector<int>g[maxn];
void add(int u,int v,int cap)
{
	edge[cnt].v=v;
	edge[cnt].cap=cap;
	edge[cnt].next=head[u];
	head[u]=cnt;
	cnt++;
}
int dijkstra()
{
	//for(int i=0;i<=n;i++)dis[i]=inf;
	memset(dis,inf,sizeof(dis));
	memset(vis,false,sizeof(vis));
    priority_queue<node>q;
    dis[1]=0;
    q.push(node(1,0));
    while(!q.empty())
    {
    	node s=q.top();q.pop();
    	int u=s.v;
    	if(vis[s.v])continue;
    	vis[s.v]=true;
    	for(int i=head[s.v];i!=-1;i=edge[i].next)
    	{
    		int v=edge[i].v;
    		int cap=edge[i].cap;
    		if(!vis[v]&&dis[v]>dis[u]+cap)
    		{
    			dis[v]=dis[u]+cap;
    			q.push(node(v,dis[v]));
			}
		}
	}
	return dis[n];
}
int main()
{
	int T;
	scanf("%d",&T);
	for(int cas=1;cas<=T;cas++)
	{   cnt=0;
	    //for(int i=0;i<3*n;i++)
		//{
		//	edge[i].v=0;
		//	edge[i].next=0;
		//	edge[i].cap=0;
		 //} 
	    memset(head,-1,sizeof(head));
	    //memset(vist,-1,sizeof(vist));
		int c,t;
		scanf("%d %d %d",&n,&m,&c);
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&t);
			//vist[t]=1;
			add(2*t-1+n,i,0);//入口 
			add(i,2*t+n,0);//出口 
		}
		for(int i=0;i<m;i++)
		{
			int u,v,cap;
			scanf("%d %d %d",&u,&v,&cap);
		    add(u,v,cap);
		    add(v,u,cap);
		}
		for(int i=1;i<=n;i++)
		{
			int u=n+i*2;
			if(i>1)
			{
				int v=u-3;
				add(u,v,c);
			}
			if(i<n)
			{
				int v=u+1;
				add(u,v,c);
			}
		}
		int ans=dijkstra();
		printf("Case #%d: %d\n",cas,ans==inf?-1:ans);
	}
}

https://cn.vjudge.net/problem/HDU-3416

非常非常好的一道题,网络流的最大流+最短路。

题解:先从A找最短路dist1,再从B找最短路dist2,然后确定最短路径包含的边,只要dist1[a[i]]+dist2[b[i]]+c[i]==dis[A][B]那么就把这条边加入网络流的队伍中,最后跑一下网络流,然后注意一个地方,跑dinic的时候,每次bfs都会改变head的值,所以每次都要cpy一遍然后再dfs,不然会T到自闭(不要问我咋知道的)

AC代码,dij也可以

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<string>
//#include<regex>
#include<cstdio>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=2010;
const int maxm=2010000;
int cnt,ccnt,n,m;
int a[maxm],b[maxm],c[maxm],deep[maxn];
struct node
{
	int to,cap,next;
	node(){}
	node(int v,int cap):to(v),cap(cap){}
	bool operator < (const node &a)const
	{
		return cap>a.cap;
	}
}edge[maxm],edge1[maxm];
int head[maxm],dis[maxn],dist1[maxn],dist2[maxn],head1[maxm],cur[maxm];
bool vis[maxn];
void add(int u,int v,int cap)
{
	edge[cnt].to=v;edge[cnt].cap=cap;edge[cnt].next=head[u];head[u]=cnt++;
	edge[cnt].to=u;edge[cnt].cap=cap;edge[cnt].next=head[v];head[v]=cnt++;
}
void addedge(int u,int v,int cap)
{
	edge1[ccnt].to=v;edge1[ccnt].cap=cap;edge1[ccnt].next=head1[u];head1[u]=ccnt++;
}
void init()
{
	cnt=0;
	memset(head,-1,sizeof(head));
}
void init1()
{
	memset(head1,-1,sizeof(head1));
	ccnt=0;
}
void dijkstra(int s)
{
	priority_queue<node>q;
	memset(dis,inf,sizeof(dis));
	memset(vis,false,sizeof(vis));
	dis[s]=0;
	q.push(node(s,0));
	while(!q.empty())
	{
		node s=q.top();q.pop();
		int u=s.to;
		//int cap=s.cap;
		if(vis[u])continue;
		vis[u]=true;
		for(int i=head1[u];i!=-1;i=edge1[i].next)
		{
			int v=edge1[i].to;
			int cap=edge1[i].cap;
			if(!vis[v]&&dis[v]>dis[u]+cap)
			{
				dis[v]=dis[u]+cap;
				q.push(node(v,dis[v]));
			}
		}
	}
}
/*void SPFA(int s)
{
	for(int i=0;i<=n;i++)
	{
		dis[i]=inf;
		vis[i]=false;
	}
	queue<int>q;
	q.push(s);
	dis[s]=0;
	vis[s]=1;
	while(!q.empty())
	{
		int s=q.front();q.pop();
		vis[s]=false;
	   for(int i=head1[s];i!=-1;i=edge1[i].next)
		{
		    int v=edge1[i].to;
		    int cap=edge1[i].cap;
			if(dis[v]>dis[s]+cap)
			{
				dis[v]=dis[s]+cap;
				if(!vis[v])
				{
					vis[v]=true;
					q.push(v);
				}
			}
		}
	}
	return ;
}*/
bool bfs(int s,int t)
{
	queue<int>q;
	memset(deep,-1,sizeof(deep));
	deep[s]=0;
	q.push(s);
	while(!q.empty())
	{
		int u=q.front();q.pop();
		for(int i=head[u];i!=-1;i=edge[i].next)
		{
			int v=edge[i].to;
			int cap=edge[i].cap;
			if(cap&&deep[v]==-1)
			{
				deep[v]=deep[u]+1;
				q.push(v);
			}
		}
	}
	if(deep[t]!=-1)return true;
	return false;
}
int dfs(int now,int t,int limit)
{
     if(now==t)return limit;
     for(int &i=cur[now];i!=-1;i=edge[i].next)
     {
     	int u=edge[i].to;
     	int cap=edge[i].cap;
     	if(cap>0&&deep[now]+1==deep[u])
     	{
     		int f=dfs(u,t,min(limit,cap));
     		    if(f>0)
     		    {
     			edge[i].cap-=f;
     			edge[i^1].cap+=f;
     			return f;
     		    }
		 }
	 }
	return 0;
}
int dinic(int s,int t)
{
	int flow=0;
	while(bfs(s,t))
	{   for(int i=1;i<=n;i++)cur[i]=head[i];
		while(1)
		{
			int f=dfs(s,t,inf);
			if(!f)break;
			flow+=f;
		}
	}
	 return flow;
}
int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d %d",&n,&m);
		int A,B;
		for(int i=0;i<m;i++)
		scanf("%d %d %d",&a[i],&b[i],&c[i]);
		scanf("%d %d",&A,&B);
		init1();
		for(int i=0;i<m;i++)
		addedge(a[i],b[i],c[i]);
		dijkstra(A); 
		//SPFA(A);
		memcpy(dist1,dis,sizeof(dis));
		init1();
		for(int i=0;i<m;i++)
		addedge(b[i],a[i],c[i]); 
		dijkstra(B); 
		//SPFA(B);
		memcpy(dist2,dis,sizeof(dis));
		init();
		for(int i=0;i<m;i++)
		if(a[i]!=b[i]&&dist1[a[i]]+dist2[b[i]]+c[i]==dist1[B])
		add(a[i],b[i],1);
		//memcpy(head3,head,sizeof(head1));
		//bfs(A,B);
		//dfs(edge3,head3,A,B);
		int ans=dinic(A,B);
		printf("%d\n",ans);
	}
	return 0;
}

亲测 SPFA和dij复杂度都够,不过建议dij,SPFA有时候会被卡。

收获很大,加油加油!!!

这两题都是建图很好的例子了

https://cn.vjudge.net/problem/HDU-4370

https://cn.vjudge.net/problem/POJ-3169

首先第一题,用思维转换成图论。

自己在草稿纸上比划一下就知道 取得点的结果一定是这样的

例如n=4时

取(1,3)->(3,2)->(2,4)不难发现,这是一条路径1->3>2>4。从1-n的一条路径

这题也就转化成最短路了;

然而,还有一种情况。(1,3)->(3,1)这样的话,这个就被独立出来了,而我们只有第一行选择了一个1,所以还有从右边的第一列选择一个1,所以也就是(4,2)->(2,4)两个加起来才满足题目的情况。其他的情况已经被一开始的最短路包含了

所以就跑两次最短路

#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
const int maxn=500;
int mp[maxn][maxn];
bool vis[maxn];
int dis[maxn];
int n;
void SPFA(int s)
{   queue<int>q;
	for(int i=1;i<=n;i++)
	{
		if(i==s)
		{
			dis[i]=inf;
			vis[i]=false;
		}
	else
	{
		dis[i]=mp[s][i];
		q.push(i);
		vis[i]=true;
	}
}
	while(!q.empty())
	{
		int u=q.front();q.pop();
		vis[u]=false;
		for(int i=1;i<=n;i++)
		{
			if(dis[i]>dis[u]+mp[u][i])
			{
				dis[i]=dis[u]+mp[u][i];
				if(!vis[i])
				{
					vis[i]=true;
					q.push(i);
				}
			}
		}
		//vis[u]=false;
	}
}
int main()
{
	while(~scanf("%d",&n))
	{
	for(int i=1;i<=n;i++)
	for(int j=1;j<=n;j++)
	scanf("%d",&mp[i][j]);
	SPFA(1);
	int ans=dis[n];
	int loop1=dis[1];
	SPFA(n);
	int loopn=dis[n];
	ans=min(ans,loop1+loopn);
	printf("%d\n",ans);
}
 } 
 
 

 

https://cn.vjudge.net/problem/POJ-3169

这一道题,不放在最短路里的话确实很难让人想到。

思路:排序肯定为1-n,那么相互吸引的增加负边,相互有好感的增加正边,跑一边最短路,用SPFA判负环。

负环出现的情况就是不可能存在正确的排列的情况。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<string>
//#include<regex>
#include<cstdio>
using namespace std;
const int maxn=1e5+10;
#define inf 0x3f3f3f3f
int dis[maxn];
bool vis[maxn];
struct node
{
	int to,cap,next;
	node(){}
	node(int to,int cap):to(to),cap(cap){}
}edge[maxn];
int head[maxn],tot,cnt[maxn],n,m,l;
void add(int u,int to,int cap)
{
	edge[tot].to=to;
	edge[tot].cap=cap;
	edge[tot].next=head[u];
	head[u]=tot++;
}
bool SPFA(int s)
{
	queue<int>q;
	memset(dis,inf,sizeof(vis));
	memset(cnt,0,sizeof(cnt));
	memset(vis,false,sizeof(vis));
	dis[s]=0;
	cnt[s]=1;
	vis[s]=true;
	q.push(s);
	while(!q.empty())
	{
		int u=q.front();q.pop();
		vis[u]=false;
		for(int i=head[u];i!=-1;i=edge[i].next)
		{
			int  v=edge[i].to;
			if(dis[v]>dis[u]+edge[i].cap)
			{
				dis[v]=dis[u]+edge[i].cap;
				if(!vis[v])
				{
					vis[v]=true;
					q.push(v);
					if(++cnt[v]>n)return false;
				}
			}
		}
	}
	return true;
}
int main()
{   memset(head,-1,sizeof(head));
	scanf("%d %d %d",&n,&m,&l);
	for(int i=0;i<m;i++)
	{
		int u,v,w;
		scanf("%d %d %d",&u,&v,&w);
		if(u>v)swap(u,v);
		add(u,v,w);
	}
	for(int i=0;i<l;i++)
	{
		int u,v,w;
		scanf("%d %d %d",&u,&v,&w);
		if(u>v)swap(u,v);
		add(v,u,-w);
	}
	if(!SPFA(1))printf("-1\n");
	else if(dis[n]==inf) printf("-2\n");
	else printf("%d\n",dis[n]);
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值