P1339 [USACO09OCT]热浪Heat Wave
链接:https://www.luogu.org/problemnew/show/P1339
题意:把牛奶从一个地方运输另外一地方的路程最短,把这个最短的路程输出来。
题解:本题就是典型的单源最短路径的问题,本题我用的是SPFA算法.
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<queue>
using namespace std;
#define Max 1<<20
int dis[2505], ma[6200][6200];
int vis[2505];
int T, C, Ts, Te;
void SPFA()
{
int i, j;
for(i = 1; i <= T; i++)
{
vis[i] = 0;
dis[i] = Max;
}
queue<int>q;
dis[Ts] = 0;
vis[Ts] = 1;
q.push(Ts);
while(!q.empty())
{
int fro = q.front();
vis[fro] = 1;
q.pop();
for(i = 1; i <= T; i++)
{
if(dis[i] > dis[fro] + ma[fro][i])
{
dis[i] = ma[fro][i] + dis[fro];
if(!vis[i])
{
vis[i] = 1;
q.push(i);
}
}
}
vis[fro] = 0;
}
}
int main()
{
int i, j;
cin>>T>>C>>Ts>>Te;
for(i = 1; i <= T; i++)
{
for(j = 1; j <= T; j++ )
{
ma[i][j] = Max;
}
ma[i][i] = 0;
}
for(i = 1; i <= C; i++)
{
int t1, t2, t3;
cin>>t1>>t2>>t3;
if(ma[t1][t2] > t3)//这个是必须的,以为从t1到t2可能路径不唯一,只需储存最小的一个
ma[t1][t2] = ma[t2][t1] = t3;
}
SPFA();
cout<<dis[Te]<<endl;
return 0;
}
P1346 电车
链接:https://www.luogu.org/problemnew/show/P1346
题意:小镇上有着一个特别的电车网络,它由一些路口和轨道组成,每个路口都连接着若干个轨道,每个轨道都通向一个路口(不排除有的观光轨道转一圈后返回路口的可能)。在每个路口,都有一个开关决定着出去的轨道,每个开关都有一个默认的状态,每辆电车行驶到路口之后,只能从开关所指向的轨道出去,如果电车司机想走另一个轨道,他就必须下车切换开关的状态。求从起点x到终点y的最少开关切换次数。
题解:一开始我没有读懂题意,以为两个地方可以相互到达那个这两个地方是不用切换开关,把这两个地方相互到达的路径设为0,其余的设为1,利用最短路径SPFA求出从x到y的最短路径,该路径就是最少的开关切换次数。显然是不对的,后来仔细理解了题意,发现从一个地方到达另外一个地方的轨道是固定的,如果还想到达别的地方就需要切换开关,所以默认从某地到达另外一个地方的初始化为0,其余为1.然后利用SPFA算法,就可以得到结果了。
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<queue>
using namespace std;
#define Max 1000000
int dis[105], ma[105][105];
int vis[105];
int n, s, t;
void SPFA()
{
queue<int>q;
int i, j;
for(i = 1; i <= n; i++)
{
dis[i] = Max;
vis[i] = 0;
}
dis[s] = 0;
vis[s] = 1;
q.push(s);
while(!q.empty())
{
int fro = q.front();
vis[fro] = 1;
q.pop();
for(i = 1; i <= n; i++)
{
if(dis[i] > dis[fro] + ma[fro][i])
{
dis[i] = dis[fro] + ma[fro][i];
if(!vis[i])
{
vis[i] = 1;
q.push(i);
}
}
}
vis[fro] = 0;
}
}
int main()
{
int i, j;
cin>>n>>s>>t;
for(i = 1; i <= n; i++)
{
for(j = 1; j <= n; j++)
{
ma[i][j] = Max;
}
ma[i][i] = 0;
}
for(i = 1; i <= n; i++)
{
int k, t;
cin>>k;
for(j = 1; j <= k; j++)
{
cin>>t;
if(j == 1)ma[i][t] = 0;//默认到达的第一个位置为0,其余为1
else ma[i][t] = 1;
}
}
SPFA();
if(dis[t] == Max)cout<<"-1"<<endl;
else cout<<dis[t]<<endl;
return 0;
}
P1119 灾后重建
链接:https://www.luogu.org/problemnew/show/P1119
题意:B地区在地震过后,所有村庄都造成了一定的损毁,而这场地震却没对公路造成什么影响。但是在村庄重建好之前,所有与未重建完成的村庄的公路均无法通车。换句话说,只有连接着两个重建完成的村庄的公路才能通车,只能到达重建完成的村庄。
题解:第一想法是每一次询问进行一个SPFA算法,虽然能得到结果,可想而知时间复非常的常,数据只通过表分之50。后来因为修复村庄的时间的单调不减的,所以可以用Floyd算法,时间复杂度远远少于前面的想法。
SPFA:
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<queue>
using namespace std;
#define Max 1000000
int dis[202], ma[202][202];
int vis[202], t[10005];
int x[10005], y[10005], w[10005];
int n, m;
void SPFA(int st){
int i, j;
for(i = 0; i < n; i++ ){
dis[i] = Max;
vis[i] = 0;
}
queue<int>q;
dis[st] = 0;
vis[st] = 1;
q.push(st);
while(!q.empty()){
int fro = q.front();
vis[fro] = 1;
q.pop();
for(i = 0; i < n; i++){
if(dis[i] > dis[fro] + ma[fro][i]){
dis[i] = dis[fro] + ma[fro][i];
if(!vis[i]){
q.push(i);
vis[i] = 1;
}
}
}
vis[fro] = 0;
}
}
int main(){
cin>>n>>m;
int i, j;
for(i = 0; i < n; i++){
for(j = 0; j < n; j++){
ma[i][j] = Max;
}
ma[i][i] = 0;
}
for(i = 0; i < n; i++)cin>>t[i];
for(i = 0; i < m; i++){
cin>>x[i]>>y[i]>>w[i];
}
int Q;
cin>>Q;
while(Q--){
int s, e, d;
cin>>s>>e>>d;
for(i = 0; i < m; i++){
if(t[x[i]] <= d && t[y[i]] <= d){
ma[x[i]][y[i]] = ma[y[i]][x[i]] = w[i];
}
}
SPFA(s);
if(dis[e] == Max)cout<<-1<<endl;
else cout<<dis[e]<<endl;
}
}
Floyd:
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<queue>
using namespace std;
#define Max 1000000
int dis[202][202];
int t[202];
int n, m, i, j;
int main()
{
cin>>n>>m;
for(i = 0; i < n; i++)
{
for(j = 0; j < n; j++ )
{
dis[i][j] = Max;
}
dis[i][i] = 0;
}
for(i = 0; i < n; i++)
{
cin>>t[i];
}
int a, b, c;
for(i = 0; i < m; i++)
{
cin>>a>>b>>c;
dis[a][b] = dis[b][a] = c;
}
int Q;
cin>>Q;
int k = 0;
while(Q--)
{
int x, y, d;
cin>>x>>y>>d;
while(t[k] <= d && k < n)
{
for(i = 0; i < n; i++)
{
for(j = 0; j < n; j++)
{
if(dis[i][j] > dis[i][k] + dis[k][j])
{
dis[i][j] = dis[i][k] + dis[k][j];
}
}
}
k++;
}
if(dis[x][y] == Max || t[x] > d || t[y] > d)//特判如果两个村庄之间没有最短路,两个村庄没有修好
cout<<"-1"<<endl;
else cout<<dis[x][y]<<endl;
}
return 0;
}
P1144 最短路计数
链接:https://www.luogu.org/problemnew/show/P1144
题意:求出所有点到初始位置1的距离。
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<queue>
#include<vector>
using namespace std;
#define Max 1000000
int dis[1000001], ans[1000001], vis[1000001];
vector<int>g[1000001];//如果用二维数组太大
int n, m, v;
void SPFA(int st){
int i;
for(i = 1; i <= n; i++){
dis[i] = Max;
vis[i] = 0;
ans[i] = 0;
}
dis[st] = 0;
vis[st] = 1;
ans[st] = 1;//本身为一个最短路
queue<int>q;
q.push(st);
while(!q.empty()){
int fro = q.front();
vis[fro] = 1;
q.pop();
for( i = 0; i < g[fro].size(); i++){
v = g[fro][i];
if(dis[v] > dis[fro] + 1){//如果满足fro到v的距离比原本到v的距离小,则更新
dis[v] = dis[fro] + 1;
ans[v] = ans[fro];
if(!vis[v]){
vis[v] = 1;
q.push(v);
}
}
else if(dis[v] == dis[fro] + 1){//这个是属于分叉,就相当于不同的路到v
ans[v] = (ans[v] + ans[fro]) % 100003;
}
}
vis[fro] = 0;
}
}
int main(){
cin>>n>>m;
for(int i = 1; i <= m; i++){
int x, y;
cin>>x>>y;
g[x].push_back(y);
g[y].push_back(x);
}
SPFA(1);
for(int i = 1; i <= n; i++)cout<<ans[i]<<endl;
return 0;
}