单源最短路的建图方式
1129. 热浪【最普通的最短路板子】

https://www.acwing.com/problem/content/1131/
详细解析
就常规的最短路,其它的没啥的特别的东西。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5*2+10;
typedef pair<int,int> PII;
int h[N],e[N],w[N],ne[N],idx;
int n,m,startx,endx,dist[N];
bool st[N];
void add(int a,int b,int c)
{
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
void Dijkstra()
{
memset(dist,0x3f,sizeof dist);
dist[startx]=0;
priority_queue<PII,vector<PII>,greater<PII>> heap; heap.push({0,startx});
while(heap.size())
{
auto t=heap.top(); heap.pop();
int u=t.second;
if(st[u]) continue;
st[u]=1;
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(dist[j]>dist[u]+w[i])
{
dist[j]=dist[u]+w[i];
heap.push({dist[j],j});
}
}
}
}
int main(void)
{
memset(h,-1,sizeof h);
cin>>n>>m>>startx>>endx;
for(int i=0;i<m;i++)
{
int a,b,c; cin>>a>>b>>c;
add(a,b,c),add(b,a,c);
}
Dijkstra();
cout<<dist[endx];
return 0;
}
1128. 信使【基本的最短路 + 稍微的扩展】

https://www.acwing.com/problem/content/description/1130/
数据范围很小,故直接用floyd算法。题目要求的就是各个点距离源点的最远的那个距离。
故直接跑一边,然后枚举每一个点到源点的距离求一个最大的即可。
#include<bits/stdc++.h>
using namespace std;
const int N=110;
int dist[N][N],n,m;
int main(void)
{
cin>>n>>m;
memset(dist,0x3f,sizeof dist);
for(int i=1;i<=n;i++) dist[i][i]=0;
while(m--)
{
int a,b,c; cin>>a>>b>>c;
dist[a][b]=dist[b][a]=min(dist[a][b],c);
}
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
dist[i][j]=min(dist[i][j],dist[i][k]+dist[k][j]);
int ans=0,flag=0;
for(int i=2;i<=n;i++)
if(dist[1][i]!=0x3f3f3f3f) ans=max(ans,dist[1][i]);
else flag=1;
if(!flag) cout<<ans<<endl;
else puts("-1");
return 0;
}
1127. 香甜的黄油【稍微变种的最短路】

https://www.acwing.com/problem/content/1129/
就枚举每一个牧场,求各个点到该点的和,在各个的和中求一个最小的和即可。
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
typedef long long int LL;
const int N=810;
int n,m,t;
int h[N],e[N*10],w[N*10],ne[N*10],idx;
int dist[N],cow[N],st[N];
LL ans=1e9;
void add(int a,int b,int c)
{
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
void Dijkstra(int startx)
{
memset(dist,0x3f,sizeof dist);
memset(st,0,sizeof st);
dist[startx]=0;
priority_queue<PII,vector<PII>,greater<PII>>heap; heap.push({0,startx});
while(heap.size())
{
auto t=heap.top(); heap.pop();
int u=t.second;
if(st[u]) continue;
st[u]=1;
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(dist[j]>dist[u]+w[i])
{
dist[j]=dist[u]+w[i];
heap.push({dist[j],j});
}
}
}
LL sum=0;
for(int i=0;i<n;i++) sum+=dist[cow[i]];
ans=min(ans,sum);
}
int main(void)
{
memset(h,-1,sizeof h);
cin>>n>>m>>t;
for(int i=0;i<n;i++) cin>>cow[i];
for(int i=0;i<t;i++)
{
int a,b,c; cin>>a>>b>>c;
add(a,b,c),add(b,a,c);
}
for(int i=1;i<=m;i++) Dijkstra(i);//枚举每一个牧场
cout<<ans<<endl;
return 0;
}
1126. 最小花费【Dijkstra 求最大乘积】

https://www.acwing.com/problem/content/1128/
详细题解
这里我们要求一个最大的乘积,才可以让花费的钱越小。因为这里的边权都是(0,1)之间的故乘以一个数必定变小
有单调性,故可以用Dijkstra来求解。
关于Dijkstra
考虑Dij为什么不行,因为Dij的最短路算法是贪心的,它贪心的选最小的并且认为这个点不会被更新了,为什么不会被更新了?因为没有负权边,从而你加入优先队列里的dis只会越来越大,毕竟一个数+一个正数一定变大,不可能以后取出来某一个点更新这个点,故我这个点取出来了,就不会再使用。Dij的正确性是基于 “放入优先队列的点的dis是单调的” 这一条件。
所以 对于求最短路,我们要求dis单调递增,即不存在负权边,对于最大乘积,我们要求乘积只允许单调变小,即乘的在0,1之间,对于最小乘积,我们要求乘积必须单调变大,即所有边权都是>=1的.
#include<bits/stdc++.h>
using namespace std;
const int N=1e5*2+10;
int h[N],e[N],ne[N],n,m,idx;
int st[N],A,B;
double w[N],dist[N];
void add(int a,int b,double c)
{
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
void Dijkstra(int A)
{
dist[A]=1.0;
priority_queue<pair<double,int>>heap; heap.push({1.0,A});
while(heap.size())
{
auto t=heap.top(); heap.pop();
int u=t.second;
if(st[u]) continue;
st[u]=1;
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(dist[j]<dist[u]*w[i])
{
dist[j]=dist[u]*w[i];
heap.push({dist[j],j});
}
}
}
}
int main(void)
{
memset(h,-1,sizeof h);
cin>>n>>m;
for(int i=0;i<m;i++)
{
int a,b,c; cin>>a>>b>>c;
add(a,b,(100-c)/100.0);
add(b,a,(100-c)/100.0);
}
cin>>A>>B;
Dijkstra(A);
printf("%.8lf",100.0/dist[B]);
return 0;
}
920. 最优乘车【根据题意建图】

https://www.acwing.com/problem/content/922/
根据每一条线,建边,注意是有向边。求最短路,最短路的结果是最少的坐车次数,减1即为最少的换乘次数。
#include<bits/stdc++.h>
using namespace std;
const int N=510;
int dist[N],g[N][N],st[N],n,m;
string s,a;
void Dijkstra()
{
memset(dist,0x3f,sizeof dist);
dist[1]=0;
for(int i=0;i<n;i++)
{
int t=-1;
for(int j=1;j<=n;j++) if(!st[j]&&(t==-1 || dist[j]<dist[t])) t=j;
st[t]=1;
for(int j=1;j<=n;j++) dist[j]=min(dist[j],dist[t]+g[t][j]);
}
}
int main(void)
{
memset(g,0x3f,sizeof g);
cin>>m>>n;
for(int i=1;i<=n;i++) g[i][i]=0;
getchar();
for(int i=0;i<m;i++)
{
getline(cin,s);
stringstream l(s);
vector<int>ve;
while(l>>a) ve.push_back(stoi(a));
for(int i=0;i<ve.size();i++)
{
for(int j=i+1;j<ve.size();j++)
{
int x=ve[i],y=ve[j];
g[x][y]=1;
}
}
}
Dijkstra();
if(dist[n]==0x3f3f3f3f) puts("NO");
else cout<<dist[n]-1;
return 0;
}
903. 昂贵的聘礼【建立虚拟结点 通过虚拟结点建图】

https://www.acwing.com/problem/content/905/
建立一个虚拟结点,通过虚拟结点和其它的各个结点建图。
最后枚举可以买到1号点的所有区间。再所有的答案中去一个最小的即可
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int g[N][N],dist[N],st[N],n,m;
int level[N],ans=1e9;
void Dijkstra(int l,int r)
{
memset(dist,0x3f,sizeof dist); dist[0]=0;
memset(st,0,sizeof st);
for(int i=0;i<n;i++)
{
int t=-1;
for(int j=0;j<=n;j++) if(!st[j]&&(t==-1 || dist[j]<dist[t])) t=j;
st[t]=1;
for(int j=0;j<=n;j++)
{
if(level[j]>=l&&level[j]<=r) dist[j]=min(dist[j],dist[t]+g[t][j]);
}
}
ans=min(ans,dist[1]);
}
int main(void)
{
memset(g,0x3f,sizeof g);
cin>>m>>n;
for(int i=1;i<=n;i++)
{
int w,t; cin>>w>>level[i]>>t;
g[0][i]=min(g[0][i],w);
for(int j=0;j<t;j++)
{
int id,price; cin>>id>>price;
g[id][i]=min(g[id][i],price);
}
}
for(int i=level[1]-m;i<=level[1];i++) Dijkstra(i,i+m);
cout<<ans;
return 0;
}
单源最短路的综合应用
1135. 新年好【最短路 + 排列组合】

https://www.acwing.com/problem/content/description/1137/
很容易的想到,枚举5!的全排列,每次Dijkstra()一遍。
这样会超时。我们可以先预处理出,每一个点到其它点的最短路。然后再爆搜,这样的话Dijkstart()只需6此即可。
大大的提升了性能。
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int N=1e5*2+10;
int h[N],e[N],w[N],ne[N],idx;
int st[N],dist[N];
int n,m,a[10];
map<PII,int>mp;
void add(int a,int b,int c)
{
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
void Dijkstra(int startx)
{
memset(dist,0x3f,sizeof dist);
dist[startx]=0;
memset(st,0,sizeof st);
priority_queue<PII,vector<PII>,greater<PII>> heap; heap.push({0,startx});
while(heap.size())
{
auto t=heap.top(); heap.pop();
int u=t.second;
if(st[u]) continue;
st[u]=1;
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(dist[j]>dist[u]+w[i])
{
dist[j]=dist[u]+w[i];
heap.push({dist[j],j});
}
}
}
}
int main(void)
{
memset(h,-1,sizeof h);
cin>>n>>m;
a[0]=1;
for(int i=1;i<=5;i++) cin>>a[i];
for(int i=0;i<m;i++)
{
int a,b,c; cin>>a>>b>>c;
add(a,b,c),add(b,a,c);
}
for(int i=0;i<=5;i++)//预处理出所有的该点到其它亲戚点的最短距离表
{
Dijkstra(a[i]);
for(int j=0;j<=5;j++)
{
mp[{a[i],a[j]}]=dist[a[j]];
mp[{a[j],a[i]}]=dist[a[j]];
}
}
int ans=1e9;
sort(a+1,a+6);
do//枚举所有的排列方式
{
int sum=0;
for(int i=1;i<=5;i++) sum+=mp[{a[i-1],a[i]}];
ans=min(ans,sum);
}while(next_permutation(a+1,a+6));
cout<<ans<<endl;
return 0;
}
340. 通信线路【二分 + 最短路】

https://www.acwing.com/problem/content/342/

上图摘自: https://www.acwing.com/solution/content/36383/
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int N=1e5+10;
int h[N],e[N*2],w[N*2],ne[N],idx;
int dist[N],st[N];
int n,m,k;
void add(int a,int b,int c) {e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;}
bool check(int mid)
{
memset(dist,0x3f,sizeof dist);
memset(st,0,sizeof st);
dist[1]=0;
priority_queue<PII,vector<PII>,greater<PII>> heap; heap.push({0,1});
while(heap.size())
{
auto t=heap.top(); heap.pop();
int u=t.second;
if(st[u]) continue;
st[u]=1;
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i],c=w[i]>mid?1:0;
//看每一条边对于整条最短路的贡献,如果大于mid说明是有贡献的为1,否则没有贡献为0
if(dist[j]>dist[u]+c)
{
dist[j]=dist[u]+c;
heap.push({dist[j],j});
}
}
}
return dist[n]<=k;//大于mid的边数小于等于k,故满足说明还可以更优
}
int main(void)
{
memset(h,-1,sizeof h);
cin>>n>>m>>k;
for(int i=0;i<m;i++)
{
int a,b,c; cin>>a>>b>>c;
add(a,b,c),add(b,a,c);
}
int l=0,r=1e6+1;
while(l<r)//二分答案
{
int mid=l+r>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
if(r==1e6+1) puts("-1");//说明没有答案
else cout<<l<<endl;
return 0;
}
342. 道路与航线【未完成 / 拓扑排序 + Dijkstra】

https://www.acwing.com/problem/content/344/
341. 最优贸易【反向建图 spfa】

https://www.acwing.com/problem/content/343/

上图摘自:y总
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
const int M=1e6+10;
int hs[N],he[N],e[M],ne[M],idx;
int w[N],n,m;
int st[N];
int minv[N],maxv[N];
void add(int h[],int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void spfa(int h[],int dist[],int type)
{
memset(st,0,sizeof st);
queue<int> q;
if(type)
{
memset(dist,-0x3f,sizeof minv);
dist[n]=w[n]; st[n]=1;
q.push(n);
}else
{
memset(dist,0x3f,sizeof maxv);
dist[1]=w[1]; st[1]=1;
q.push(1);
}
while(q.size())
{
int u=q.front(); q.pop();
st[u]=false;
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(!type&&dist[j]>min(dist[u],w[j]))
{
dist[j]=min(dist[u],w[j]);
if(!st[j]) q.push(j),st[j]=1;
}
if(type&&dist[j]<max(dist[u],w[j]))
{
dist[j]=max(dist[u],w[j]);
if(!st[j]) q.push(j),st[j]=1;
}
}
}
}
int main(void)
{
memset(hs,-1,sizeof hs);
memset(he,-1,sizeof he);
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>w[i];
for(int i=0;i<m;i++)
{
int a,b,op; cin>>a>>b>>op;
add(hs,a,b),add(he,b,a);
if(op==2) add(hs,b,a),add(he,a,b);
}
spfa(hs,minv,0);
spfa(he,maxv,1);
int ans=0;
for(int i=1;i<=n;i++) ans=max(ans,maxv[i]-minv[i]);//枚举所有的点,求以该点作为交易点
cout<<ans;
return 0;
}
单源最短路的扩展应用
1137. 选择最佳线路【反向建图】

https://www.acwing.com/problem/content/1139/
方法一: 反向建图即可。
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int N=1e3+10;
int dist[N],st[N],n,m,s;
int h[N],e[N*20],w[N*20],ne[N*20],idx;
int a[N],t;
void add(int a,int b,int c)
{
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
void Dijkstra(int s)
{
memset(dist,0x3f,sizeof dist);
memset(st,0,sizeof st);
dist[s]=0;
priority_queue<PII,vector<PII>,greater<PII>>heap; heap.push({0,s});
while(heap.size())
{
auto t=heap.top(); heap.pop();
int u=t.second;
if(st[u]) continue;
st[u]=1;
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(dist[j]>dist[u]+w[i])
{
dist[j]=dist[u]+w[i];
heap.push({dist[j],j});
}
}
}
}
int main(void)
{
while(cin>>n>>m>>s)
{
memset(h,-1,sizeof h);
idx=0;
for(int i=0;i<m;i++)
{
int a,b,c; cin>>a>>b>>c;
add(b,a,c);
}
cin>>t;
for(int i=0;i<t;i++) cin>>a[i];
Dijkstra(s);
int ans=0x3f3f3f3f;
for(int i=0;i<t;i++) ans=min(ans,dist[a[i]]);
if(ans==0x3f3f3f3f) puts("-1");
else cout<<ans<<endl;
}
return 0;
}
方法二: 建立虚拟结点。
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int N=1e4;
int dist[N],st[N],n,m,s;
int h[N],e[N*20],w[N*20],ne[N*20],idx;
int a[N],t;
void add(int a,int b,int c)
{
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
void Dijkstra(int s)
{
memset(dist,0x3f,sizeof dist);
memset(st,0,sizeof st);
dist[s]=0;
priority_queue<PII,vector<PII>,greater<PII>>heap; heap.push({0,s});
while(heap.size())
{
auto t=heap.top(); heap.pop();
int u=t.second;
if(st[u]) continue;
st[u]=1;
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(dist[j]>dist[u]+w[i])
{
dist[j]=dist[u]+w[i];
heap.push({dist[j],j});
}
}
}
}
int main(void)
{
while(cin>>n>>m>>s)
{
memset(h,-1,sizeof h);
idx=0;
for(int i=0;i<m;i++)
{
int a,b,c; scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
}
cin>>t;
for(int i=0;i<t;i++) cin>>a[i],add(0,a[i],0);
Dijkstra(0);
int ans=dist[s];
if(ans==0x3f3f3f3f) puts("-1");
else cout<<ans<<endl;
}
return 0;
}
1131. 拯救大兵瑞恩【未完成 拆点 很重要可以试试】

https://www.acwing.com/problem/content/1133/
1134. 最短路计数【统计最短路的数量】

https://www.acwing.com/problem/content/1136/
无边权就是全部的边权为1。这里只能用bfs和Dijkstra不能用spfa。
因为bfs只入队一次,出队一次是一个拓扑序。
Dijkstra也是同样如此。而spfa则不是。因为这里的边权都为1故第一次到达就是最短路故可以用bfs
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
const int M=1e5*5+10;
const int mod=100003;
int h[N],e[M],ne[M],cnt[N],idx;
int n,m,dist[N];
void add(int a,int b) {e[idx]=b,ne[idx]=h[a],h[a]=idx++;}
void bfs()
{
memset(dist,0x3f,sizeof dist);
dist[1]=0,cnt[1]=1;
queue<int>q; q.push(1);
while(q.size())
{
int u=q.front(); q.pop();
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(dist[j]>dist[u]+1)
{
dist[j]=dist[u]+1;
cnt[j]=cnt[u];
q.push(j);
}else if(dist[j]==dist[u]+1) cnt[j]=(cnt[j]+cnt[u])%mod;
}
}
}
int main(void)
{
memset(h,-1,sizeof h);
cin>>n>>m;
for(int i=0;i<m;i++)
{
int a,b; cin>>a>>b;
add(a,b),add(b,a);
}
bfs();
for(int i=1;i<=n;i++) cout<<cnt[i]<<endl;
return 0;
}
383. 观光【拆点 可以搞】

https://www.acwing.com/problem/content/385/
Floyd算法
floyd的应用场景
- 1.最短路
- 2.传递闭包
- 3.找最小环
- 4.恰好经过k条边的最短路,倍增。
1125. 牛的旅行【floyd一个好的扩展】

https://www.acwing.com/problem/content/1127/
#include<bits/stdc++.h>
using namespace std;
const int N=200;
const double INF=1e12;
double maxv[N],dist[N][N];
string s[N];
int n,x,y;
map<int, pair<double,double> >mp;
double get(int a,int b)
{
int x=mp[a].first,y=mp[a].second;
int xx=mp[b].first,yy=mp[b].second;
return sqrt((x-xx)*(x-xx)+(y-yy)*(y-yy));
}
void init()
{
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
if(s[i][j]=='1') dist[i][j]=get(i,j);
else if(i!=j) dist[i][j]=INF;
}
}
void folyd()
{
for(int k=0;k<n;k++)
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]);
}
int main(void)
{
cin>>n;
for(int i=0;i<n;i++) cin>>x>>y,mp[i]={x,y};
for(int i=0;i<n;i++) cin>>s[i];
init(),folyd();
double res1=0,res2=INF;
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
if(dist[i][j]>INF/2) continue;//不可达,说明不是一个集合
maxv[i]=max(maxv[i],dist[i][j]);
res1=max(res1,maxv[i]);
}
}
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
if(dist[i][j]>INF/2) res2=min(res2,maxv[i]+get(i,j)+maxv[j]);
printf("%.6lf",max(res1,res2));
return 0;
}
343. 排序【floyd传递闭包】

https://www.acwing.com/problem/content/description/345/
g[i][j]==0 说明没关系 g[i][j]==1 说明 i<j
#include<bits/stdc++.h>
using namespace std;
const int N=30;
int g[N][N],n,m,st[N];
void folyd()
{
for(int k=0;k<n;k++)
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
g[i][j]|=g[i][k]&&g[k][j];
}
int check()
{
for(int i=0;i<n;i++) if(g[i][i]) return 2;//矛盾
for(int i=0;i<n;i++)
for(int j=0;j<i;j++)
if(!g[i][j]&&!g[j][i]) return 0;//还没有完整
return 1;//完整了
}
char get_min()
{
for(int i=0;i<n;i++)//枚举所有的可能的最小值
{
if(!st[i])
{
bool flag=true;
for(int j=0;j<n;j++)
{
if(!st[j]&&g[j][i])//如果没选的有比其更小的
{
flag=false;
break;
}
}
if(flag) //说明没有比其更小的了
{
st[i]=true;
return 'A'+i;
}
}
}
}
int main(void)
{
while(cin>>n>>m,n||m)
{
memset(g,0,sizeof g);
memset(st,0,sizeof st);
int type=0,t=0;
for(int i=1;i<=m;i++)
{
string s; cin>>s;
int a=s[0]-'A',b=s[2]-'A';
if(!type)//还可以继续
{
g[a][b]=1;
folyd();
type=check();
if(type) t=i;
}
}
if(!type) puts("Sorted sequence cannot be determined.");
else if (type == 2) printf("Inconsistency found after %d relations.\n", t);
else
{
printf("Sorted sequence determined after %d relations: ", t);
for (int i = 0; i < n; i ++ ) printf("%c", get_min());
printf(".\n");
}
}
return 0;
}
344. 观光之旅【未完成】

https://www.acwing.com/problem/content/346/
345. 牛站【未完成】

https://www.acwing.com/problem/content/347/
最小生成树
1140. 最短网络【最基本的最小生成树的板子】

基本的板子没啥说的
prim()算法
#include<bits/stdc++.h>
using namespace std;
const int N=110;
int g[N][N],n,st[N],res;
int dist[N];
void prim()
{
memset(dist,0x3f,sizeof dist);
dist[1]=0;
for(int i=0;i<n;i++)
{
int t=-1;
for(int j=1;j<=n;j++) if(!st[j]&&(t==-1 || dist[j]<dist[t])) t=j;
st[t]=1;
res+=dist[t];
for(int j=1;j<=n;j++) dist[j]=min(dist[j],g[t][j]);
}
}
int main(void)
{
cin>>n;
memset(g,0x3f,sizeof g);
for(int i=1;i<=n;i++) g[i][i]=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
cin>>g[i][j];
prim();
cout<<res;
return 0;
}
kruskal算法
#include<bits/stdc++.h>
using namespace std;
const int N=110;
int a[N][N],n,p[N];
struct node{int a,b,c;};
bool cmp(node a,node b) {return a.c<b.c;}
vector<node>ve;
int find(int x)
{
if(x!=p[x]) p[x]=find(p[x]);
return p[x];
}
int f()
{
int res=0;
for(int i=1;i<=n;i++) p[i]=i;
sort(ve.begin(),ve.end(),cmp);
for(int i=0;i<ve.size();i++)
{
int a=ve[i].a,b=ve[i].b,c=ve[i].c;
if(find(a)!=find(b))
{
res+=c;
p[find(a)]=find(b);
}
}
return res;
}
int main(void)
{
cin>>n;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++) cin>>a[i][j];
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(i==j) continue;
ve.push_back({i,j,a[i][j]});
ve.push_back({j,i,a[i][j]});
}
}
cout<<f();
return 0;
}
1141. 局域网【最小生成树的一个简单的变种】

https://www.acwing.com/problem/content/1143/
总的和减去最小生成树的值即为答案。即加的是不是最小生成树上的边的和。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
struct node {int a,b,c;};
bool cmp(node a,node b) {return a.c<b.c;}
int p[N],n,m;
vector<node>ve;
int find(int x)
{
if(x!=p[x]) p[x]=find(p[x]);
return p[x];
}
int f()
{
int res=0;
sort(ve.begin(),ve.end(),cmp);
for(int i=0;i<ve.size();i++)
{
int a=ve[i].a,b=ve[i].b,c=ve[i].c;
if(find(a)!=find(b)) p[find(b)]=find(a);
else res+=c;
}
return res;
}
int main(void)
{
cin>>n>>m;
for(int i=1;i<=n;i++) p[i]=i;
for(int i=0;i<m;i++)
{
int a,b,c; cin>>a>>b>>c;
ve.push_back({a,b,c});
}
cout<<f()<<endl;
return 0;
}
1142. 繁忙的都市 【简单的变种】

https://www.acwing.com/problem/content/1144/
就是求一下边数,和最小生成树上的最长的边。边数是n-1这是可以直接得到的。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
struct node{int a,b,c;};
int n,m,cnt,maxv,p[N];
vector<node>ve;
bool cmp(node a,node b) {return a.c<b.c;}
int find(int x)
{
if(x!=p[x]) p[x]=find(p[x]);
return p[x];
}
void f()
{
for(int i=1;i<=n;i++) p[i]=i;
sort(ve.begin(),ve.end(),cmp);
for(int i=0;i<ve.size();i++)
{
int a=ve[i].a,b=ve[i].b,c=ve[i].c;
if(find(a)!=find(b))
{
cnt++;
maxv=max(maxv,c);
p[find(b)]=find(a);
}
}
}
int main(void)
{
cin>>n>>m;
for(int i=0;i<m;i++)
{
int a,b,c; cin>>a>>b>>c;
ve.push_back({a,b,c});
}
f();
cout<<cnt<<" "<<maxv;
return 0;
}
1143. 联络员【简单的最小生成树的变种】

https://www.acwing.com/problem/content/1145/
先将必选的弄好,剩下的就是普通的板子。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int p[N],n,m;
struct node{int a,b,c;};
bool cmp(node a,node b) {return a.c<b.c;}
vector<node>ve1,ve2;
int find(int x)
{
if(x!=p[x]) p[x]=find(p[x]);
return p[x];
}
int solve()
{
for(int i=1;i<=n;i++) p[i]=i;
sort(ve1.begin(),ve1.end(),cmp);
sort(ve2.begin(),ve2.end(),cmp);
int sum=0;
for(int i=0;i<ve1.size();i++)
{
int a=ve1[i].a,b=ve1[i].b,c=ve1[i].c;
p[find(b)]=find(a);
sum+=c;
}
for(int i=0;i<ve2.size();i++)
{
int a=ve2[i].a,b=ve2[i].b,c=ve2[i].c;
if(find(a)!=find(b))
{
p[find(b)]=find(a);
sum+=c;
}
}
return sum;
}
int main(void)
{
cin>>n>>m;
for(int i=0;i<m;i++)
{
int op,a,b,c; cin>>op>>a>>b>>c;
if(op==1) ve1.push_back({a,b,c});
else ve2.push_back({a,b,c});
}
cout<<solve()<<endl;
return 0;
}
1144. 连接格点【好的变种最小生成树】

https://www.acwing.com/problem/content/1146/
1144. 连接格点【最小生成树】
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int p[N],n,m;
int x,y,xx,yy;
struct node{int a,b,c;};
bool cmp(node a,node b) {return a.c<b.c;}
vector<node>ve1,ve2;
int find(int x)
{
if(x!=p[x]) p[x]=find(p[x]);
return p[x];
}
int get(int x,int y)
{
return x*m+y;
}
void solve(int x,int y)//存所有的边
{
int dx[4]={-1,0,1,0};
int dy[4]={0,1,0,-1};
for(int i=0;i<4;i++)
{
int tempx=x+dx[i];
int tempy=y+dy[i];
if(tempx<1||tempx>n||tempy<1||tempy>m) continue;
if(get(x-1,y)<get(tempx-1,tempy))//a<b时才加可以避免重复加
{
if(dy[i]) ve2.push_back({get(x-1,y),get(tempx-1,tempy),2});
else ve1.push_back({get(x-1,y),get(tempx-1,tempy),1});
}
}
}
void init()//初始化
{
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++) p[get(i-1,j)]=get(i-1,j);
}
int f()
{
int res=0;
for(int i=0;i<ve1.size();i++)
{
int a=ve1[i].a,b=ve1[i].b,c=ve1[i].c;
if(find(a)!=find(b))
{
p[find(a)]=find(b);
res+=c;
}
}
for(int i=0;i<ve2.size();i++)
{
int a=ve2[i].a,b=ve2[i].b,c=ve2[i].c;
if(find(a)!=find(b))
{
p[find(a)]=find(b);
res+=c;
}
}
return res;
}
int main(void)
{
scanf("%d%d",&n,&m);
init();
while(scanf("%d%d%d%d",&x,&y,&xx,&yy)!=EOF)
{
int a=get(x-1,y),b=get(xx-1,yy);
if(find(a)!=find(b)) p[find(a)]=find(b);
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++) solve(i,j);
printf("%d\n",f());
return 0;
}
最小生成树的扩展应用
1146. 新的开始【虚拟节点 和 最小生成树】

https://www.acwing.com/problem/content/1148/
#include<bits/stdc++.h>
using namespace std;
struct node{int a,b,c;};
bool cmp(node a,node b) {return a.c<b.c;}
const int N=1e3+10;
int a[N],p[N],n,c;
vector<node>ve;
int find(int x)
{
if(x!=p[x]) p[x]=find(p[x]);
return p[x];
}
int f()
{
int res=0;
for(int i=0;i<=n;i++) p[i]=i;
sort(ve.begin(),ve.end(),cmp);
for(int i=0;i<ve.size();i++)
{
int a=ve[i].a,b=ve[i].b,c=ve[i].c;
if(find(a)!=find(b))
{
p[find(a)]=find(b);
res+=c;
}
}
return res;
}
int main(void)
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i],ve.push_back({0,i,a[i]});//创建虚拟结点
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
cin>>c;
if(c) ve.push_back({i,j,c});
}
cout<<f()<<endl;
return 0;
}
1145. 北极通讯网络【扩展】

https://www.acwing.com/problem/content/description/1147/
本质就是删除最短路上的(k-1)条边 输出第k大的边
#include<bits/stdc++.h>
using namespace std;
const int N=510;
int n,k,p[N];
struct node
{
int a,b;
double c;
};
int find(int x)
{
if(x!=p[x]) p[x]=find(p[x]);
return p[x];
}
bool cmp(node a,node b) {return a.c<b.c;}
vector<node>ans;
vector<pair<double,double>>ve;
double get(int x,int y,int xx,int yy){return sqrt(1.0*(x-xx)*(x-xx)+(y-yy)*(y-yy));}
double solve()
{
for(int i=0;i<n;i++) p[i]=i;
sort(ans.begin(),ans.end(),cmp);
vector<double>s;//存一下所有的边
for(int i=0;i<ans.size();i++)
{
int a=ans[i].a,b=ans[i].b;
double c=ans[i].c;
if(find(a)!=find(b))
{
p[find(a)]=find(b);
s.push_back(c);
}
}
if(k==n) return 0;
reverse(s.begin(),s.end());
return s[k-1];//输出从大到小第k的边
}
int main(void)
{
cin>>n>>k;
for(int i=0;i<n;i++)
{
double x,y; cin>>x>>y;
ve.push_back({x,y});
}
for(int i=0;i<ve.size();i++)
{
for(int j=i+1;j<ve.size();j++)
{
double x=ve[i].first,y=ve[i].second;
double xx=ve[j].first,yy=ve[j].second;
ans.push_back({i,j,get(x,y,xx,yy)});
}
}
printf("%.2lf",solve());
return 0;
}
346. 走廊泼水节【经典题 求构成完全图的最小权和】

https://www.acwing.com/problem/content/348/
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int p[N],cnt[N],t,n;
struct node{int a,b,c;};
bool cmp(node a,node b){return a.c<b.c;}
vector<node>ve;
int find(int x)
{
if(x!=p[x]) p[x]=find(p[x]);
return p[x];
}
int solve()
{
for(int i=1;i<=n;i++) p[i]=i,cnt[i]=1;
sort(ve.begin(),ve.end(),cmp);
int sum=0;
for(int i=0;i<ve.size();i++)
{
int a=ve[i].a,b=ve[i].b,c=ve[i].c;
if(find(a)!=find(b))
{
int pa=find(a),pb=find(b);
sum+=(cnt[pa]*cnt[pb]-1)*(c+1);
//俩连通块构成完全图 cnt[pa]*cnt[pb]-1
//-1是因为去除已经有的那条边
cnt[pb]+=cnt[pa];
p[pa]=pb;
}
}
return sum;
}
int main(void)
{
cin>>t;
while(t--)
{
cin>>n;
ve.clear();
for(int i=1;i<=n-1;i++)
{
int a,b,c; cin>>a>>b>>c;
ve.push_back({a,b,c});
}
cout<<solve()<<endl;
}
return 0;
}
1148. 秘密的牛奶运输【次小生成树 未完成】

https://www.acwing.com/problem/content/description/1150/

负环
求负环的常用方法,基于SPFA:
- (1)拥挤每个点入队的次数,如果某个点入队n次,则说明存在负环。
- (2)统计当前每个点的最短路中所包含的变数,如果某个点的最短路所包含的变数
大于等于n,则说明存在环。
剪枝:当所有的点的入队次数超过2n时,我们就认为图中有很大可能是存在负环的。
904. 虫洞【变种的判负环】

https://www.acwing.com/problem/content/906/
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int h[N],e[N],w[N],ne[N],idx;
int dist[N];
int t,n,m1,m2;
void add(int a,int b,int c)
{
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
void init()
{
memset(h,-1,sizeof h);
idx=0;
}
bool spfa()
{
queue<int>q;
int cnt[N]={0};
int st[N]={0};
memset(dist,0,sizeof dist);
for(int i=1;i<=n;i++) q.push(i),st[i]=1;
while(q.size())
{
int u=q.front(); q.pop();
st[u]=0;
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(dist[j]>dist[u]+w[i])
{
dist[j]=dist[u]+w[i];
cnt[j]=cnt[u]+1;
if(cnt[j]>=n) return true;
if(!st[j]) q.push(j);
}
}
}
return false;
}
int main(void)
{
cin>>t;
while(t--)
{
init();
cin>>n>>m1>>m2;
while(m1--)
{
int a,b,c; cin>>a>>b>>c;
add(a,b,c),add(b,a,c);
}
while(m2--)
{
int a,b,c; cin>>a>>b>>c;
add(a,b,-c);
}
if(spfa()) puts("YES");
else puts("NO");
}
return 0;
}
361. 观光奶牛【判断正环 + 二分】

https://www.acwing.com/problem/content/363/
详细题解
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;
int h[N],e[N],wt[N],ne[N],idx;
int st[N],wf[N],cnt[N],n,m;
double dist[N];
void add(int a,int b,int c)
{
e[idx]=b,wt[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
bool check(double mid)
{
memset(dist,0,sizeof dist);
memset(st,0,sizeof st);
memset(cnt,0,sizeof cnt);
queue<int>q;
for(int i=1;i<=n;i++) q.push(i),st[i]=1;
while(q.size())
{
int u=q.front(); q.pop();
st[u]=0;
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(dist[j]<dist[u]+wf[u]-mid*wt[i])
{
dist[j]=dist[u]+wf[u]-mid*wt[i];
if(!st[j]) q.push(j);
cnt[j]=cnt[u]+1;
if(cnt[j]>=n) return true;
}
}
}
return false;
}
int main(void)
{
memset(h,-1,sizeof h);
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>wf[i];
while(m--)
{
int a,b,c; cin>>a>>b>>c;
add(a,b,c);
}
double l=0,r=1000;
while(r-l>1e-4)
{
double mid=(l+r)/2;
if(check(mid)) l=mid;
else r=mid;
}
printf("%.2lf",l);
return 0;
}
1165. 单词环【未完成】

https://www.acwing.com/problem/content/1167/
差分约束

1169. 糖果

https://www.acwing.com/problem/content/description/1171/

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
typedef long long int LL;
int h[N],e[N],w[N],ne[N],idx;
int dist[N],st[N],cnt[N];
int n,m;
void add(int a,int b,int c) {e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;}
bool spfa()
{
stack<int>q; q.push(0);
memset(dist,-0x3f,sizeof dist);
dist[0]=0,st[0]=1;
while(q.size())
{
int u=q.top(); q.pop();
st[u]=0;
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(dist[j]<dist[u]+w[i])
{
dist[j]=dist[u]+w[i];
cnt[j]=cnt[u]+1;
if(cnt[j]>=n+1) return false;
if(!st[j]) q.push(j),st[j]=1;
}
}
}
return true;
}
int main(void)
{
memset(h,-1,sizeof h);
cin>>n>>m;
for(int i=0;i<m;++i)
{
int op,a,b; scanf("%d%d%d",&op,&a,&b);
if(op==1) add(a,b,0),add(b,a,0);
else if(op==2) add(a,b,1);
else if(op==3) add(b,a,0);
else if(op==4) add(b,a,1);
else add(a,b,0);
}
for(int i=1;i<=n;++i) add(0,i,1);
if(!spfa()) puts("-1");
else
{
LL res=0;
for(int i=1;i<=n;++i) res+=dist[i];
cout<<res;
}
return 0;
}
有向图的强连通分量
对于一个有向图,**连通分量:**对于分量中任意两点u,v。必然可以从u走到v,且从v走到u。
强联通分量: 极大的联通分量。
求联通分量常用的算法模板是:tarjan算法
拓扑排序
1191. 家谱树【基本的拓扑的板子】

https://www.acwing.com/problem/content/1193/
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int h[N],e[N],ne[N],d[N],idx;
void add(int a,int b) {e[idx]=b,ne[idx]=h[a],h[a]=idx++;}
int n,m;
vector<int>ans;
void topsort()
{
queue<int>q;
for(int i=1;i<=n;i++) if(!d[i]) q.push(i),ans.push_back(i);
while(q.size())
{
int u=q.front(); q.pop();
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(--d[j]==0) q.push(j),ans.push_back(j);
}
}
}
int main(void)
{
memset(h,-1,sizeof h);
cin>>n;
for(int i=1;i<=n;i++) while(cin>>m,m) add(i,m),d[m]++;
topsort();
for(int i=0;i<ans.size();i++) cout<<ans[i]<<" ";
return 0;
}
1192. 奖金

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int h[N],e[N],ne[N],idx;
int st[N],cnt[N],n,m,ans[N];
void add(int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
vector<int>ve;
bool topsort()
{
queue<int>q;
for(int i=1;i<=n;i++) if(!cnt[i]) q.push(i),ve.push_back(i),st[i]=1;
while(q.size())
{
int u=q.front(); q.pop();
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(--cnt[j]==0)
{
if(!st[j]) q.push(j),ve.push_back(j),st[j]=1;
}
}
}
if(ve.size()==n) return true;
return false;
}
int main(void)
{
memset(h,-1,sizeof h);
cin>>n>>m;
for(int i=0;i<m;i++)
{
int a,b; cin>>a>>b;
add(a,b);
cnt[b]++;
}
if(topsort())
{
int sum=0;
for(int i=ve.size()-1;i>=0;i--)
{
ans[ve[i]]=100;
for(int j=h[ve[i]];j!=-1;j=ne[j]) ans[ve[i]]=max(ans[ve[i]],ans[e[j]]+1);
sum+=ans[ve[i]];
}
cout<<sum;
}else puts("Poor Xed");
return 0;
}
8015

被折叠的 条评论
为什么被折叠?



