一.B3647 【模板】Floyd
思路:虽然这题是Floyd的模版题,但我使用使用范围更广的dijkstra算法(迪杰斯特拉算法)做的。
代码:
//最短路径问题 迪杰斯特拉算法
//无向图的短路径。
#include<bits/stdc++.h>
using namespace std;
int M = 0x7FFFFFFF;
int n,m,a[101][101],min1,next1=77;
int vis[101],dis[101];
int terminal1[101];
void min_distance (int star)
{
for (int i=1;i<=n;i++) //初始化
{
//terminal1[i];
dis[i]=M;
vis[i]=0;
}
terminal1[star]=0;
dis[star]=0;
vis[star]=1;
while (star <= n)
{
min1 = M;
for (int i=1;i<=n;i++)
{
if (a[star][i]!= M && vis[i]==0)
dis[i]= min(dis[i],a[star][i]+dis[star]);
if (vis[i]==0 && dis[i] < min1)
{
next1=i;
min1=dis[i];
}
}
//cout<<next<<" "<<min1<<endl;
if (min1==M) break;
terminal1[next1]=min1;
vis[next1]=1;
star = next1;
}
for (int i=1;i<=n;i++) cout<<terminal1[i]<<" ";
cout<<"\n";
return ;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
for (int i=1;i<=n;i++) //初始化
{
dis[i]=M;
for (int j=1;j<=n;j++)
a[i][j]=M;
}
while (m--) //用邻接矩阵保存图的信息
{
int u,v,w;
cin>>u>>v>>w;
a[u][v]=min(a[u][v],w); //严谨
a[v][u]=a[u][v]; //无向图
}
for (int i=1;i<=n;i++) min_distance [i];
return 0;
}
二.P4779 【模板】单源最短路径(标准版)
思路:这题我一开始是用上面一样的方法做的,但本题数据过多,用邻接矩阵来存图可能会超时和超内存以及WA,所以我使用向前星链表来存图,并使用优先队列来替代每次的找与本次start点最近的点,这样可以缩短时间复杂度。
代码:
#include<bits/stdc++.h>
using namespace std;
const int MaxN = 100010, MaxM = 500010;
struct edge
{
int to, dis, next;
};
edge e[MaxM];
int head[MaxN], dis[MaxN], cnt;
bool vis[MaxN];
int n, m, s;
inline void add_edge( int u, int v, int d )
{
cnt++;
e[cnt].dis = d;
e[cnt].to = v;
e[cnt].next = head[u];
head[u] = cnt;
}
struct node
{
int dis;
int pos;
bool operator <( const node &x )const
{
return x.dis < dis;
}
};
std::priority_queue<node> q;
inline void dijkstra()
{
dis[s] = 0;
q.push( ( node ){0, s} );
while( !q.empty() )
{
node tmp = q.top();
q.pop();
int x = tmp.pos, d = tmp.dis;
if( vis[x] )
continue;
vis[x] = 1;
for( int i = head[x]; i; i = e[i].next )
{
int y = e[i].to;
if( dis[y] > dis[x] + e[i].dis && !vis[y])
{
dis[y] = dis[x] + e[i].dis;
if( 1 )
{
q.push( ( node ){dis[y], y} );
}
}
}
}
}
int main()
{
scanf( "%d%d%d", &n, &m, &s );
for(int i = 1; i <= n; ++i)dis[i] = 0x7fffffff;
for( register int i = 0; i < m; ++i )
{
register int u, v, d;
scanf( "%d%d%d", &u, &v, &d );
add_edge( u, v, d );
}
dijkstra();
for( int i = 1; i <= n; i++ )
printf( "%d ", dis[i] );
return 0;
}
三.P2661 [NOIP2015 提高组] 信息传递
思路:并查集求最小环
把每个同学看成一个点,信息的传递就是在他们之间连有向边,游戏轮数就是求最小环。
图论求最小环,我在里面看到了并查集。
假如说信息由A传递给B,那么就连一条由A指向B的边,同时更新A的父节点,A到它的父节点的路径长也就是B到它的父节点的路径长+1。
这样我们就建立好了一个图,之后信息传递的所有环节都按照这些路径。游戏结束的轮数,也就是这个图里最小环的长度。
如果有两个点祖先节点相同,那么就可以构成一个环,长度为两个点到祖先节点长度之和+1。
代码
#include<cstdio>
#include<iostream>
using namespace std;
int f[200002],d[200002],n,minn,last; //f保存祖先节点,d保存到其祖先节点的路径长。
int fa(int x)
{
if (f[x]!=x) //查找时沿途更新祖先节点和路径长。
{
int last=f[x]; //记录父节点(会在递归中被更新)。
f[x]=fa(f[x]); //更新祖先节点。
d[x]+=d[last]; //更新路径长(原来连在父节点上)。
}
return f[x];
}
void check(int a,int b)
{
int x=fa(a),y=fa(b); //查找祖先节点。
if (x!=y) {f[x]=y; d[a]=d[b]+1;} //若不相连,则连接两点,更新父节点和路径长。
else minn=min(minn,d[a]+d[b]+1); //若已连接,则更新最小环长度。
return;
}
int main()
{
int i,t;
scanf("%d",&n);
for (i=1;i<=n;i++) f[i]=i; //祖先节点初始化为自己,路径长为0。
minn=0x7777777;
for (i=1;i<=n;i++)
{
scanf("%d",&t);
check(i,t); //检查当前两点是否已有边相连接。
}
printf("%d",minn);
return 0;
}
本题解来自:题解 P2661 【信息传递】 - anyway - 洛谷博客 (luogu.org);感谢提供此简单方便的解法。
四.P1144 最短路计数
思路:与第2题类似。
注意有坑,小心memset()里面不要用0x7fffffff,会爆的,只能用0x3f3f3f3f。原因是无穷的加法。我在这里卡了好久!!!
代码:
#include<bits/stdc++.h>
using namespace std;
#define M 0x7FFFFFFF
#define mod 100003
const int maxn = 1e6+2;
const int maxm = 2e6+2;
int n,m;
struct edge
{
int to,w,nex;
} e[maxm] ;
int cnt=0;
int js[maxn],dis[maxn],head[maxn];
struct node
{
int fro , dis;
bool operator < (const node &x) const
{
return dis > x.dis;
}
};
priority_queue <node> q;
inline void add_edge( int u, int v, int d )
{
cnt++;
e[cnt].w = d;
e[cnt].to = v;
e[cnt].nex = head[u];
head[u] = cnt;
}
int main()
{
cin>>n>>m;
for( int i=1; i<=m; i++ )
{
register int u, v;
scanf( "%d%d", &u, &v );
add_edge( u, v, 1 );
add_edge( v, u, 1 );
}
//cout<<n<<endl;
//memset(dis,0x3f,n);
/*for(int i=1;i<=n;i++)
cout<<dis[i]<<'\n'; */
memset(dis,0x3f,sizeof(dis));
dis[1]=0;
js[1]=1;
q.push((node){1,0});
node a;
while(!q.empty()) //dijkstra经典套路操作。
{
a=q.top();
q.pop();
int nf=a.fro,dd=a.dis;
//if(dd>=dis[nf]) continue;
//if(dd!=dis[nf]) continue;
for(int i=head[nf];i;i=e[i].nex)
{
int d=e[i].w;
int nto=e[i].to;
if(dd+e[i].w==dis[nto]) //一边计算一边模。
js[nto]=(js[nf]+js[nto])%mod;
if (dis[nto] > dis[nf]+d)
{
dis[nto]=dis[nf]+d;
js[nto]=js[nf]; //找到一条更短的路径是,用它的前驱的js换它。
q.push((node){nto,dis[nto]});
// q.push((node){v,dis[v]});
}
}
}
for(int i=1;i<=n;i++)
cout<<js[i]<<'\n';
return 0;
}
五.P8794 [蓝桥杯 2022 国 A] 环境治理
思路:用二分法来求最少要经过多少天后 P 指标可以满足要求。
代码:
#include<bits/stdc++.h>
using namespace std;
int limit[102][102],n,q,d[102][102];
int dijst(int d[][102])
{
int m=0;
for (int k=0;k<n;k++)
{
int vis[102],dis[102];
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
int c=0,min1,s=k,nex;
vis[s]=1;
dis[s]=0;
while (c<n-1)
{
min1=0x3f3f3f3f;
for (int i=0;i<n;i++)
{
if (vis[i]==0)
dis[i] = min(dis[i],d[s][i]+dis[s]);
if (vis[i]==0 && dis[i]<min1)
{
min1=dis[i];
nex=i;
}
}
//if (min1==0x3f3f3f3f) break;
s=nex;
vis[nex]=1;
//cout<<min1<<endl;
//cout<<nex<<endl;
c++;
m+=min1;
}
//cout<<m<<endl;
}
return m;
}
int calc(int day) // 计算经过day 天治理后的P指标值
{
int tmp[102][102]; // 用于治理的辅助数组
int i,j,k;
for (i =0; i<n; i++)
for (j =0; j<n; j++)
tmp[i][j]=d[i][j];
for (i =0; i<n; i++)
{
int val = day/n+(day%n>=i+1?1:0);
for (j =0 ; j<n; j++)
{
tmp[i][j]-=val;
if (tmp[i][j]<limit[i][j]) tmp[i][j]=limit[i][j];
tmp[j][i]-=val;
if (tmp[j][i]<limit[j][i]) tmp[j][i]=limit[j][i];
}
}
return dijst(tmp)<=q;
}
int main()
{
cin>>n>>q;
for (int i=0;i<n;i++)
for (int j=0;j<n;j++)
cin>>d[i][j];
for (int i=0;i<n;i++)
for (int j=0;j<n;j++)
cin>>limit[i][j];
//cout<<dijst(d)<<endl;
if (dijst(d)<=q)
{
cout<<0;
return 0;
}
// 二分搜索
int left=1,right=100000*n-1 ,mid,ans;
while (left<=right)
{
mid=(left+right)>>1;
//mid = (left+right)/2;
if (calc(mid))
{
right= mid -1;
ans = mid;
}
else
left=mid+1;
}
if (calc(ans))
cout<<ans;
else
cout<<-1;
return 0;
}