图论合集
最小生成树
kruskal
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=2e5+10;
ll n,m,f[N],l,cnt,ans;
struct line{
ll x,y,l;
}li[N];
bool cmp(line x,line y){
return x.l<y.l;
}
void init(){
for(ll i=1;i<=n;i++){
f[i]=i;
}
}
ll find(ll x){
if(f[x]==x) return x;
return f[x]=find(f[x]);
}
int main(){
cin >> n >> m;
for(ll i=1;i<=m;i++){
scanf("%lld%lld%lld",&li[i].x,&li[i].y,&li[i].l);
}
sort(li+1,li+m+1,cmp);
init();
for(ll i=1;i<=m;i++){
if(find(li[i].x)==find(li[i].y)) continue;
cnt++;
ans+=li[i].l;
f[find(li[i].x)]=find(li[i].y);
if(cnt==n) break;
}
cout << ans;
return 0;
}
最短路
dijkstra
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=5e5+5;
const ll INF=1e18+7;
ll n,m,s;
vector<ll> e[N],u[N];
ll d[N],t[N];
priority_queue<pair<ll,ll>,vector<pair<ll,ll> >,greater<pair<ll,ll> > >pq;
void dijkstra(){
fill(d+1,d+n+1,INF);
d[s]=0;
pq.push(make_pair(0, s));
while(pq.size()){
ll h1=pq.top().first;
ll h2=pq.top().second;
pq.pop();
if(t[h2]) continue;
t[h2]=1;
for(ll i=0;i<e[h2].size();i++){
if(h1+u[h2][i]<d[e[h2][i]]){
d[e[h2][i]]=h1+u[h2][i];
pq.push(make_pair(h1+u[h2][i],e[h2][i]));
}
}
}
}
int main(){
scanf("%lld%lld",&n,&m);
s=1;
for(ll i=1;i<=m;i++){
ll x,y,z;
scanf("%lld%lld%lld",&x,&y,&z);
e[x].push_back(y);
u[x].push_back(z);
}
dijkstra();
for(ll i=1;i<=n;i++){
if(d[i]==INF) printf("inf ");
else printf("%lld ",d[i]);
}
return 0;
}
SPFA
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;
const int INF=1e9+7;
int n,m,s,d[N],l[N],in[N];
int fi[N],ne[N],to[N],ew[N],cnt;
queue<int> q;
bool spfa(){
fill(d,d+N,INF);
d[s]=0;
q.push(s);
while(q.size()){
int u=q.front();
q.pop();
in[u]=0;
for(int i=fi[u];i;i=ne[i]){
if(d[u]+ew[i]<d[to[i]]){
l[to[i]]=l[u]+1;
if(l[to[i]]>n){
return 0;
}
d[to[i]]=d[u]+ew[i];
if(!in[to[i]]) q.push(to[i]);
}
}
}
return 1;
}
void add(int x,int y,int z){
to[++cnt]=y;
ew[cnt]=z;
ne[cnt]=fi[x];
fi[x]=cnt;
}
int main(){
scanf("%d%d%d",&n,&m,&s);
int x,y,z;
for (int i=1;i<=m;i++) {
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
}
if (spfa()) {
for (int i=1;i<n;i++) printf("%d ",d[i]);
printf("%d\n",d[n]);
}
else {
printf("-1\n");
}
return 0;
}
缩点
#include<bits/stdc++.h>
using namespace std;
const int maxn=10000+15;
int n,m,sum,tim,top,s;
int p[maxn],head[maxn],sd[maxn],dfn[maxn],low[maxn];
int stac[maxn],instack[maxn];
int h[maxn],in[maxn],dist[maxn];
struct EDGE
{
int to;int next;int from;
}edge[maxn*10],ed[maxn*10];
void add(int x,int y)
{
edge[++sum].next=head[x];
edge[sum].from=x;
edge[sum].to=y;
head[x]=sum;
}
void tarjan(int x)
{
low[x]=dfn[x]=++tim;
stac[++top]=x;instack[x]=1;
for (int i=head[x];i;i=edge[i].next)
{
int v=edge[i].to;
if (!dfn[v])
{
tarjan(v);
low[x]=min(low[x],low[v]);
}
else if (instack[v])
{
low[x]=min(low[x],dfn[v]);
}
}
if (dfn[x]==low[x])
{
int y;
while (y=stac[top--])
{
sd[y]=x;
instack[y]=0;
if (x==y) break;
p[x]+=p[y];
}
}
}
int topo()
{
queue <int> q;
int tot=0;
for (int i=1;i<=n;i++)
if (sd[i]==i&&!in[i])
{
q.push(i);
dist[i]=p[i];
}
while (!q.empty())
{
int k=q.front();q.pop();
for (int i=h[k];i;i=ed[i].next)
{
int v=ed[i].to;
dist[v]=max(dist[v],dist[k]+p[v]);
in[v]--;
if (in[v]==0) q.push(v);
}
}
int ans=0;
for (int i=1;i<=n;i++)
ans=max(ans,dist[i]);
return ans;
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
scanf("%d",&p[i]);
for (int i=1;i<=m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
add(u,v);
}
for (int i=1;i<=n;i++)
if (!dfn[i]) tarjan(i);
for (int i=1;i<=m;i++)
{
int x=sd[edge[i].from],y=sd[edge[i].to];
if (x!=y)
{
ed[++s].next=h[x];
ed[s].to=y;
ed[s].from=x;
h[x]=s;
in[y]++;
}
}
printf("%d",topo());
return 0;
}
例题
[最小生成树]USACO08OCT Watering Hole G
链接:https://www.luogu.com.cn/problem/P1550
建造水源虚拟点
#include <bits/stdc++.h>
using namespace std;
const int N=500;
int n,a[N],e[N][N],f[N],l,cnt,ans;
struct line{
int x,y,l;
}li[N*N];
bool cmp(line x,line y){
return x.l<y.l;
}
void init(){
for(int i=1;i<=n+1;i++){
f[i]=i;
}
}
int find(int x){
if(f[x]==x) return x;
return f[x]=find(f[x]);
}
int main(){
cin >> n;
for(int i=1;i<=n;i++) cin >> a[i];
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cin >> e[i][j];
}
}
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
li[++l]={i, j, e[i][j]};
}
}
for(int i=1;i<=n;i++){
li[++l]={i,n+1, a[i]};
}
sort(li+1,li+l+1,cmp);
init();
for(int i=1;i<=l;i++){
if(find(li[i].x)==find(li[i].y)) continue;
cnt++;
ans+=li[i].l;
f[find(li[i].x)]=find(li[i].y);
if(cnt==n) break;
}
cout << ans;
return 0;
}
[缩点] USACO03FALL / HAOI2006 受欢迎的牛 G
链接:https://www.luogu.com.cn/problem/P2341
#include <bits/stdc++.h>
using namespace std;
const int N=5e4+4;
int n,m,cnt;
stack<int>s;
int fi[N],ne[N],to[N];
int dfn[N],low[N];
int du[N],id[N],all[N];
int ins[N],tnt,g;
void add(int x,int y){
to[++cnt]=y;
ne[cnt]=fi[x];
fi[x]=cnt;
}
void tarjan(int x){
dfn[x]=low[x]=++tnt;
s.push(x);
ins[x]=1;
for(int i=fi[x];i;i=ne[i]){
int u=to[i];
if(!dfn[u]){
tarjan(u);
low[x]=min(low[x],low[u]);
}
else if(ins[u]){
low[x]=min(low[x],dfn[u]);
}
}
int k;
if(low[x]==dfn[x]){
g++;
do{
k=s.top();
s.pop();
ins[k]=0;
id[k]=g;
all[g]++;
}while(x!=k);
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int x,y;
scanf("%d%d",&x,&y);
add(x, y);
}
for(int i=1;i<=n;i++){
if(!dfn[i]) tarjan(i);
}
for(int i=1;i<=n;i++){
for(int j=fi[i];j;j=ne[j]){
int u=to[j];
if(id[i]!=id[u]){
du[id[i]]++;
}
}
}
int t=0;
for(int i=1;i<=g;i++){
if(!du[i]){
if(t) {
printf("0\n");
return 0;
}
t=i;
}
}
printf("%d\n",all[t]);
return 0;
}
[基环树] NOIP2018 提高组 旅行
#include <bits/stdc++.h>
using namespace std;
const int N=10005;
int fi[N],ne[N],to[N],fo[N],l,cnt,v[N],dep;
vector<int> e[N];
int ans[N],k[N];
int n,m,l1,l2;
int read()//快读模板
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
void add(int x,int y){ //链式前向星
ne[++l]=fi[x];
to[l]=y;
fo[l]=x;
fi[x]=l;
}
void dfs1(int x){
cnt++;
ans[cnt]=x;
v[x]=1;
for(int i=0;i<e[x].size();i++){
if(v[e[x][i]]==0){
dfs1(e[x][i]);
}
}
}
void dfs2(int x,int f){
if(v[x]) return;
v[x]=1;
k[++dep]=x;//打擂台答案
for(int i=0;i<e[x].size();i++){
int r=e[x][i];
if(r==f) continue;
if(x==l1&&e[x][i]==l2||x==l2&&e[x][i]==l1){
continue; // 重复边
}
dfs2(e[x][i],x);
}
}
bool check(){
for(int i=1;i<=n;i++){
if(k[i]==ans[i]) continue;
if(k[i]>ans[i]) return 0;
return 1;
}
}
void change(){ // 将打擂台答案复制
for(int i=1;i<=n;i++) ans[i]=k[i];
}
int main(){
n=read();m=read();
for(int i=1;i<=m;i++){
int x,y;
x=read();y=read();
// 链式前向星与vector存储
e[x].push_back(y);
e[y].push_back(x);
add(x, y);
add(y, x);
}
for(int i=1;i<=n;i++){//由于字典序,需要将所有边排序
sort(e[i].begin(),e[i].end());
}
if(n==m+1){//普通的树
dfs1(1);
for(int i=1;i<=n;i++){
printf("%d ",ans[i]);
}
}
else{//基环树
for(int i=0;i<l;i+=2){//基环树不符合贪心,需要一个一个边枚举
dep=0;
memset(v, 0, sizeof(v));
l1=fo[i];l2=to[i];
dfs2(1,-1);
if(dep<n){
continue;
//没有遍历所有节点
}
if(ans[1]==0) change(); //没答案,将打擂台的答案复制
else if(check()) change(); //打擂台答案比原答案字典序小
}
for(int i=1;i<=n;i++){
printf("%d ",ans[i]);
}
}
return 0;
}