P4779 【模板】单源最短路径(标准版)
思路:图论,优先队列
#include <bits/stdc++.h>
using namespace std;
int n, m, s, ans[100005];
bool vis[100005];
struct node{
int u, v;
bool operator<(const node& d) const{
return v > d.v;
}
bool operator>(const node& d) const{
return v < d.v;
}
};
vector<node> vec[100005];
priority_queue<node, vector<node> > q;
int main(){
scanf("%d %d %d", &n, &m, &s);
for(int i = 0; i < m; i++){
int a, b, c;
scanf("%d %d %d", &a, &b, &c);
node d{b, c};
vec[a].push_back(d);
}
for(int i = 1; i <= n; i++){
ans[i] = 0x3f3f3f3f;
}
ans[s] = 0;
node d{s, 0};
q.push(d);
while(!q.empty()){
node cur = q.top();
q.pop();
if(vis[cur.u]) continue;
vis[cur.u] = true;
for(int i = 0; i < vec[cur.u].size(); i++){
if(ans[vec[cur.u][i].u] > ans[cur.u] + vec[cur.u][i].v){
ans[vec[cur.u][i].u] = ans[cur.u] + vec[cur.u][i].v;
node tmp = {vec[cur.u][i].u, ans[vec[cur.u][i].u]};
q.push(tmp);
}
}
}
for(int i = 1; i <= n; i++){
printf("%d ", ans[i]);
}
return 0;
}
B3647 【模板】Floyd
思路:最短路
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
int Map[105][105];
int main()
{
int n,m,u,v,w;
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
if(i==j)
Map[i][j]=0;
else
Map[i][j]=inf;
}
while(m--)
{
scanf("%d%d%d",&u,&v,&w);
Map[u][v]=min(Map[u][v],w);
Map[v][u]=Map[u][v];
}
int i,j,k;
for(k=1;k<=n;k++)
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
if(Map[i][j]>Map[i][k]+Map[k][j])
Map[i][j]=Map[i][k]+Map[k][j];
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
printf("%d ",Map[i][j]);
}
printf("\n");
}
return 0;
}
P2661 [NOIP2015 提高组] 信息传递
思路:图论
#include <bits/stdc++.h>
using namespace std;
const int Max=200005;
int n,m,size,Index,cnt,tot,ans=1e9;
int num[Max],low[Max],sum[Max],p[Max<<1],vis[Max],first[Max];
struct shu{int to,next;};
shu edge[Max];
inline int get_int()
{
int x=0,f=1;
char c;
for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar());
if(c=='-') f=-1,c=getchar();
for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
return x*f;
}
inline void build(int x,int y)
{
edge[++size].next=first[x];
first[x]=size;
edge[size].to=y;
}
inline void tarjan(int point)
{
num[point]=low[point]=++Index;
p[++tot]=point,vis[point]=1;
for(int u=first[point];u;u=edge[u].next)
{
int to=edge[u].to;
if(!num[to]) tarjan(to),low[point]=min(low[point],low[to]);
else if(vis[to]) low[point]=min(low[point],num[to]);
}
if(low[point]==num[point])
{
cnt++;
while(1)
{
int x=p[tot--];
sum[cnt]++;
vis[x]=0;
if(x==point) break;
}
}
}
int main()
{
n=get_int();
for(int i=1;i<=n;i++) build(i,get_int());
for(int i=1;i<=n;i++) if(!num[i]) tarjan(i);
for(int i=1;i<=cnt;i++) if(sum[i]!=1) ans=min(ans,sum[i]);
cout<<ans<<"\n";
return 0;
}
P1144 最短路计数
思路:图论
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e6+8;
int head[MAXN],num,s=1;
int n,m,dis[MAXN],cnt[MAXN];
struct edge
{
int to,next;
}e[MAXN*2];
struct node
{
int x,dis;
bool operator<(node a)const
{
return dis>a.dis;
}
};
priority_queue<node> q;
void add(int u,int v)
{
e[++num].to=v;
e[num].next=head[u];
head[u]=num;
}
void dijkstra()
{
memset(dis,0x3f3f3f3f,sizeof dis);
dis[s]=0;
q.push((node){s,0});
cnt[1]=1;
while(!q.empty())
{
int u=q.top().x;
q.pop();
for(int i=head[u];i;i=e[i].next)
{
int v=e[i].to;
if(dis[v]>dis[u]+1)
{
dis[v]=dis[u]+1;
q.push((node){v,dis[v]});
cnt[v]=cnt[u]%100003;
}
else if(dis[v]==dis[u]+1)
{
cnt[v]=(cnt[u]+cnt[v])%100003;
}
}
}
}
int main()
{
cin>>n>>m;
for(int i=1,u,v;i<=m;i++)
{
cin>>u>>v;
add(u,v);
add(v,u);
}
dijkstra();
for(int i=1;i<=n;i++) cout<<cnt[i]<<endl;
return 0;
}
P8794 [蓝桥杯 2022 国 A] 环境治理
思路:二分,最短路
#include<bits/stdc++.h>
using namespace std;
#define N 105
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define mid (l + r >> 1)
int n, Q, l, r, ans = INF;
int D[N][N], L[N][N], dis[N][N], reduce[N];
inline int check(int x) {
int t = x / n, P = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
dis[i][j] = INF;
for (int i = 1; i <= n; i++)
dis[i][i] = 0;
for (int i = 1; i <= n; i++)
reduce[i] = t;
for (int i = 1; i <= x - n * t; i++)
++reduce[i];
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
dis[i][j] = max(L[i][j], D[i][j] - reduce[i] - reduce[j]);//与每条道路都有的一个灰尘度的下限值比较取最小值
for(int k=1;k<=n;k++)
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
P += dis[i][j];
if (P <= Q) return 1;
else return 0;
}
signed main() {
cin >> n >> Q;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
cin >> D[i][j];
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
cin >> L[i][j];
l = 0, r = INF;
while (l <= r) {
if (check(mid)) ans = min(ans, mid), r = mid - 1;
else l = mid + 1;
}
if (ans == INF) printf("-1");
else printf("%lld", ans);
return 0;
}
P3367 【模板】并查集
思路:并查集
#include<bits/stdc++.h>
using namespace std;
const int MAXcount = 100000;
int par[MAXcount],height[MAXcount];
//par[i]表示i结点的父结点 “par[i]=-1”表示为根结点
//height[i]表示以i为根结点的树的结点个数
int Find(int x)//返回x的父亲结点
{
int ini = x;
while(1){
if(par[x]==-1){
//朴素
// return x;
break;
}
x=par[x];
}
//优化--路径压缩,再重复一次查找根结点操作,
//每查找完一次父亲结点,
//就将该结点的父亲par[]设置为该树的根结点
//(也就是上一次循环查找到的x)
while(1){
if(par[ini]==-1){
break;;
}
int zj = ini;
ini=par[ini];
par[zj]=x;
}
return x;
}
void Union(int x,int y)
{
int rootx,rooty;
rootx=Find(x);rooty=Find(y);
if(rootx!=rooty){//如果根结点不同,进行合并操作
//朴素--从x和y集合的根结点中随机选取一个作为另一个集合的根结点,完成合并操作
// par[rooty]=par[rootx];
//优化--选择树高小的合并到树高(集合的结点个数)大的根结点下
if(height[rooty]>=height[rootx]){//如果y集合的结点数大于x集合的结点数,则将x集合合并到y结合的根结点下
par[rootx]=rooty;
height[rooty]+=height[rootx];//同时更新树高
}else{
par[rooty]=rootx;
height[rootx]+=height[rooty];
}
}
}
int main()
{
int N,M;
memset(par,-1,sizeof(par));//初始化为-1,每个结点刚开始都是一个集合,所以其父结点为-1,表示其为根结点
memset(height, 0, sizeof(height));//初始化数组为0
cin>>N>>M;
for(int i=0;i<M;i++){
int z,x,y;
cin>>z>>x>>y;
if(z==1){
Union(x,y);//合并集合x和y
}else{
int roox=Find(x);//查找x结点的父亲结点
int rooy=Find(y);//查找y结点的父亲结点
if(roox==rooy)cout<<"Y"<<endl;//比较两结点的父亲结点是否相同,可知是否在同一集合之中
else cout<<"N"<<endl;
}
}
}
P8604 [蓝桥杯 2013 国 C] 危险系数
思路:图论
#include<iostream>
#include<vector>
#define int long long
using namespace std;
vector<int>edges[1010];//临接表存储图
vector<bool>visited(1010);//记录是否走过
vector<int>vexnum(1010);//记录每个点走过次数
int n, m, start, endd, ans = 0, sum = 0;
void dfs(int cur) {
if (cur == endd) {
sum++;//路径总数
for (int i = 1; i <= n; i++) {
if (visited[i]==1) {
vexnum[i]++;//每个点走过次数
}
}
}
else {
int len = edges[cur].size();
for (int i = 0; i < len; i++) {
int value = edges[cur][i];
if (!visited[value]) {
visited[value] = 1;
dfs(value);
visited[value] = 0;
}
}
}
}
signed main()
{
cin >> n >> m;
int v1, v2, len;
for (int i = 0; i < m; i++) {
cin >> v1 >> v2;
edges[v1].push_back(v2);
edges[v2].push_back(v1);
}
cin >> start >> endd;
visited[start] = 1;
dfs(start);
if (sum > 0) {
for (int i = 1; i <= n; i++) {
if (vexnum[i] == sum) {//是否存在点走过次数与路径总数相等的点
ans++;//关键点计数
}
}
cout << ans - 2 << endl;//减2是因为统计时多加了起点和终点
}
else {
cout << "-1" << endl;
}
return 0;
}
P1330 封锁阳光大学
思路:图论
#include <bits/stdc++.h>
using namespace std;
const int maxv=1e4+10;
const int maxe=1e5+10;
inline int read(){
int x=0,f=1;char ch;
do{ch=getchar();if(ch=='-')f=-1;}while(!isdigit(ch));
do{x=x*10+ch-'0';ch=getchar();}while(isdigit(ch));
return f*x;
}
struct node{
int nxt,to;
}e[maxe<<1];
int tot=1,head[maxv];
int n,m,ans,sum[2];
bool vis[maxv],cor[maxv];
inline void add_edge(int from,int to){
e[++tot]=node{head[from],to},head[from]=tot;
}
void read_and_parse(){
n=read(),m=read();
for(int i=1;i<=m;i++){
int from=read(),to=read();
add_edge(from,to),add_edge(to,from);
}
}
bool dfs(int u,int c){
if(vis[u])return cor[u]==c;
vis[u]=1,++sum[cor[u]=c];
for(int i=head[u];i;i=e[i].nxt)if(!dfs(e[i].to,c^1))return 0;
return 1;
}
void solve(){
for(int i=1;i<=n;i++)if(!cor[i]){
sum[0]=sum[1]=0;
if(!dfs(i,0)){puts("Impossible");return;}
ans+=min(sum[0],sum[1]);
}
printf("%d\n",ans);
}
int main(){
read_and_parse();
solve();
return 0;
}
P3916 图的遍历
思路:图论
#include <iostream>
#include <algorithm>
#include <queue>
#include <cstring>
#define Max 100005
using namespace std;
vector<int > Graph[Max];
int flag[Max];
void dfs(int v,int t);
int a[Max];
int main()
{
int n,m,v1,v2;
cin>>n>>m;
for(int i=1;i<=m;i++)
{
cin>>v1>>v2;
Graph[v2].push_back(v1);
}
memset(flag, false, sizeof(flag));
int v;
for(int i=n;i;i--)
{
if(!flag[i])
{
dfs(i,i);
}
}
for(int i=1;i<=n;i++)
{
cout<<a[i]<<" ";
}
return 0;
}
void dfs(int v,int t)
{
flag[v]=true;
a[v]=t; //遍历所有当前能达到的点
for(int i=0;i<Graph[v].size();i++)
{
if(!flag[Graph[v][i]])
{
dfs(Graph[v][i],t);
}
}
}
P1119 灾后重建
思路:图论
#include <bits/stdc++.h>
using namespace std;
int M,N,Q;
int t[205]; //存储重建时间
int dist[205][205]; //存公路长度
void floyd(int k) //以前k个村庄为中转点进行dp
{
for(int i = 0; i < N; i ++)
{
for(int j = 0; j < N; j ++)
{
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
}
}
return;
}
int main()
{
cin >> N >> M; //村庄数和公路数
for(int i = 0; i < N; i ++) cin >> t[i];
for(int i = 0; i < N; i ++)
{
for(int j = 0 ;j < N; j ++)
if(i != j)dist[i][j]=0x3f3f3f; //给两个村庄之间的边长初始化成无穷大
}
for(int i = 0; i < M; i ++) //用邻接矩阵来存边
{
int a,b,c;
cin >> a >> b >> c;
dist[a][b] = c;
dist[b][a] = c;
}
cin >> Q; //询问次数
int temp = 0; //代表能访问到的序号最大的村庄
for(int i = 1; i <= Q; i ++)
{
int a,b,c;
cin >> a >> b >> c;
while(temp < N && t[temp] <= c) //如果这天村庄已经重建好了,并且村庄序号没有越界
{
floyd(temp++); //以前temp个村庄作为中转点
}
if(c < t[a] || c < t[b]) cout << -1; //没重建好直接GG
else if(dist[a][b] == 0x3f3f3f) cout << -1; //两村庄不连通也直接GG
else cout << dist[a][b];
if(i != Q) cout << endl;
}
return 0;
}