AcWing 图论打卡

树图的存储

邻接矩阵

就二维数组 不用说

邻接表

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
#include<cstring>
#include<unordered_map>
using namespace std;
typedef long long LL;
const int N=10000,M=2*N;
int h[N],e[M],ne[M],idx;
void add(int a,int b){
    e[idx]=b;   
    ne[idx]=h[a];//next指针
    h[a]=idx++;
}
int main(){
    memset(h,-1,sizeof h);
  return 0;
}
//  freopen("testdata.in", "r", stdin);

846. 树的重心

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

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 100010, M = N * 2;

int n;
int h[N], e[M], ne[M], idx;
int ans = N;
bool st[N];

void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}

int dfs(int u)
{
    st[u] = true;

    int size = 0, sum = 0;
    for (int i = h[u]; i != -1; i = ne[i])
    {
        int j = e[i];
        if (st[j]) continue;

        int s = dfs(j);
        size = max(size, s);
        sum += s;
    }

    size = max(size, n - sum - 1);
    ans = min(ans, size);

    return sum + 1;
}

int main()
{
    scanf("%d", &n);

    memset(h, -1, sizeof h);

    for (int i = 0; i < n - 1; i ++ )
    {
        int a, b;
        scanf("%d%d", &a, &b);
        add(a, b), add(b, a);
    }

    dfs(1);

    printf("%d\n", ans);

    return 0;
}

847. 图中点的层次

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

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
#include<cstring>
#include<unordered_map>
using namespace std;
typedef long long LL;
const int N=100010,M=100010;
int h[N],e[M],ne[M],idx;
int d[N];
int n,m;
int ans=-1;
void add(int a,int b){
    e[idx]=b;
    ne[idx]=h[a];
    h[a]=idx++;
}
queue<int>q;
int main(){
    memset(h,-1,sizeof h);
    memset(d,-1,sizeof d);
    cin>>n>>m;
    for(int i=0;i<m;i++){
        int a,b;
        cin>>a>>b;
        add(a,b);
    }
    d[1]=0;
    q.push(1);
    while(q.size()){
        int t=q.front();
        q.pop();
        for(int i=h[t];i!=-1;i=ne[i]){
            int j=e[i];
            if(d[j]==-1){
                d[j]=d[t]+1;
                q.push(j);
            }
        }
    }
    cout<<d[n]<<endl;
  return 0;
}
//  freopen("testdata.in", "r", stdin);

有向图的拓扑序列

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

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
#include<cstring>
#include<unordered_map>
using namespace std;
typedef long long LL;
const int N=1e5+5;
int h[N],ne[N],e[N],idx;
int ru[N];
bool st[N];
int n,m;
vector<int >ans;
void add(int a,int b){
    e[idx]=b;
    ne[idx]=h[a];
    h[a]=idx++;
}
void Delete(int u){
    for(int i=h[u];i!=-1;i=ne[i]){
        int j=e[i];
        ru[j]--;
    }
    st[u]=true;
    return ;
}
int main(){
    memset(h,-1,sizeof h);
    cin>>n>>m;
    for(int i=0;i<m;i++){
        int a,b;
        cin>>a>>b;
        add(a,b);
        ru[b]++;
    }
    while(1){
        int flag=0;
        for(int i=1;i<=n;i++){
            if(ru[i]==0 && !st[i]){
                flag=1;
                Delete(i);
                ans.push_back(i);
            }
        }
        if(!flag){
            break;
        }
    }
    if(ans.size()<n) puts("-1");
    else {
        for(int i=0;i<ans.size();i++){
            cout<<ans[i]<<" ";
        }
    }
  return 0;
}
//  freopen("testdata.in", "r", stdin);

849. Dijkstra求最短路 I

https://www.acwing.com/problem/content/851/
朴素做法:

1.dist[1]=0  dist[else]=INF;
2.循环n次
每次找未使用的距离最近的点(和1号点的距离)
用该点去更新他能去的边的距离
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
#include<cstring>
#include<unordered_map>
using namespace std;
typedef long long LL;
const int N=505;
int g[N][N];
int dist[N];
bool st[N];
int n,m;
int fun(){
    memset(dist,0x3f,sizeof dist);
    dist[1]=0;
    for(int i=0;i<n-1;i++){
            int t=-1;
        for(int j=1;j<=n;j++){
            if(!st[j]&&(t==-1 || dist[t]>dist[j])){
                t=j;
            }
        }
        for(int j=1;j<=n;j++){
            dist[j]=min(dist[j],dist[t]+g[t][j]);
        }
        st[t]=true;
    }
    if(dist[n]==0x3f3f3f3f) return -1;
    return dist[n];
}
int main(){
    memset(g,0x3f,sizeof g);
    cin>>n>>m;
    for(int i=0;i<m;i++){
        int a,b,c;
        cin>>a>>b>>c;
        g[a][b]=min(g[a][b],c);
    }
    cout<<fun()<<endl;
  return 0;
}
//  freopen("testdata.in", "r", stdin);

850. Dijkstra求最短路 II

https://www.acwing.com/problem/content/852/
通过优先队列小根堆 优化每次找到和1距离最近的点的操作

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
#include<cstring>
#include<unordered_map>
using namespace std;
typedef long long LL;
typedef pair<int,int>PII;
const int N=1e6+5;
int h[N],e[N],w[N],ne[N],idx;
int dist[N];
bool st[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++;
}
int fun(){
    memset(dist,0x3f,sizeof dist);
    priority_queue<PII,vector<PII>,greater<PII>> heap;
    dist[1]=0;
    heap.push({0,1});
    while(heap.size()){
        auto temp=heap.top();
        heap.pop();
        int ver=temp.second;
        if(st[ver]) continue;
        st[ver]=true;
        for(int i=h[ver];i!=-1;i=ne[i]){
            int j=e[i];
            if(dist[j]>dist[ver]+w[i]){
                dist[j]=dist[ver]+w[i];
                heap.push({dist[j],j});
            }
        }
    }
    if(dist[n]==0x3f3f3f3f) return -1;
    else return dist[n];
}
int main(){
    cin>>n>>m;
    memset(h,-1,sizeof h);
    while(m--){
        int a,b,c;
        cin>>a>>b>>c;
        add(a,b,c);
    }
    cout<<fun()<<endl;
  return 0;
}
//  freopen("testdata.in", "r", stdin);

853. 有边数限制的最短路

https://www.acwing.com/problem/content/855/
bellman-ford算法过程:
1.循环n次
2.遍历所有的边 a->b=w
3.dist[b]=min(dist[b],dist[a]+w);
这题由于有边的限制,需要备份一下数组。保证只更新一次

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
#include<cstring>
#include<unordered_map>
using namespace std;
typedef long long LL;
const int M=1e5+5;
struct S{
    int a;
    int b;
    int w;
}s[M];
int n,m,k;
int dist[550];
int last[550];
int main(){
    cin>>n>>m>>k;
    for(int i=0;i<=m;i++){
        int a,b,c;
        cin>>a>>b>>c;
        s[i].a=a,s[i].b=b,s[i].w=c;
    }
    memset(dist,0x3f,sizeof dist);
    dist[1]=0;
    for(int i=0;i<k;i++){
            memcpy(last, dist, sizeof dist);
        for(int j=0;j<m;j++){
            int a=s[j].a;
            int b=s[j].b;
            int w=s[j].w;
            if(dist[b]>last[a]+w){
                dist[b]=last[a]+w;
            }
        }
    }
    if (dist[n] > 0x3f3f3f3f / 2) puts("impossible");
    else cout<<dist[n];
  return 0;
}
//  freopen("testdata.in", "r", stdin);

851. spfa求最短路

https://www.acwing.com/problem/content/853/
spfa思路:
基于BF算法的优化
对于dist[b]=min(dist[b],dist[a]+w);
dist[b]想被更新前提一定是dist[a]被更新
开一个队列去保存所有被更新的a
然后取队列的a去更新b
写法和迪杰斯特拉算法几乎一样

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
#include<cstring>
#include<unordered_map>
using namespace std;
typedef long long LL;
typedef pair<int,int>PII;
const int N=1e6+5;
int h[N],e[N],w[N],ne[N],idx;
int dist[N];
bool st[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++;
}
void fun(){
    memset(dist,0x3f,sizeof dist);
    dist[1]=0;
    st[1]=true;
    queue<int >q;
    q.push(1);
    while(q.size()){
        int k=q.front();
        st[k]=false;
        q.pop();
        for(int i=h[k];i!=-1;i=ne[i]){
            int j=e[i];
            if(dist[j]>dist[k]+w[i]){
                dist[j]=dist[k]+w[i];
                if(!st[j]){//队列中没有j
                    q.push(j);
                    st[j]=true;
                }
            }
        }
    }
}
int main(){
    cin>>n>>m;
    memset(h,-1,sizeof h);
    while(m--){
        int a,b,c;
        cin>>a>>b>>c;
        add(a,b,c);
    }
    fun();
    if (dist[n] > 0x3f3f3f3f / 2) puts("impossible");
    else cout<<dist[n]<<endl;
  return 0;
}
//  freopen("testdata.in", "r", stdin);

852. spfa判断负环

https://www.acwing.com/problem/content/854/
注意题意说明没有从1开始,所以吧每个点都加入队列

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
#include<cstring>
#include<unordered_map>
using namespace std;
typedef long long LL;
typedef pair<int,int>PII;
const int N=1e6+5;
int h[N],e[N],w[N],ne[N],idx;
int dist[N];
int cnt[N];
bool st[N];//标记当前队列有哪些元素
int n,m;
bool add(int a,int b,int c){
    e[idx]=b;
    w[idx]=c;
    ne[idx]=h[a];
    h[a]=idx++;
}
bool fun(){
    memset(dist,0x3f,sizeof dist);
    queue<int >q;
    for(int i=1;i<=n;i++){
        st[i]=true;
        q.push(i);
    }
    while(q.size()){
        int k=q.front();
        st[k]=false;
        q.pop();
        for(int i=h[k];i!=-1;i=ne[i]){
            int j=e[i];
            if(dist[j]>dist[k]+w[i]){
                dist[j]=dist[k]+w[i];
                cnt[j]=cnt[k]+1;
                if(cnt[j]>=n) return true;
                if(!st[j]){//队列中没有j
                    q.push(j);
                    st[j]=true;
                }
            }
        }
    }
    return false;
}
int main(){
    cin>>n>>m;
    memset(h,-1,sizeof h);
    while(m--){
        int a,b,c;
        cin>>a>>b>>c;
        add(a,b,c);
    }
    if (fun()) puts("Yes");
    else puts("No");
  return 0;
}
//  freopen("testdata.in", "r", stdin);

Prim算法求最小生成树

https://www.acwing.com/problem/content/860/
prim算法过程:

1.初始化dist[]=INF;
2.循环n次,每次找到距离集合最近的点t,然后用该点t更新其他点到**集合**的距离
3.吧t加入集合
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
#include<cstring>
#include<unordered_map>
using namespace std;
typedef long long LL;
const int N=510;
const int INF=0x3f3f3f3f;
int g[N][N];
int dist[N];
bool st[N];
int n,m;
long long ans;
int prim(){
    int res=0;
    memset(dist,0x3f,sizeof dist);
    for(int i=0;i<n;i++){
        int t=-1;
        for(int j=1;j<=n;j++){
            if(!st[j] && (t==-1 || dist[t]>dist[j])){
                t=j;
            }
        }
        if(i && dist[t]==INF) return INF;
        if(i) res+=dist[t];
        st[t]=true;
        for(int j=1;j<=n;j++){
            dist[j]=min(dist[j],g[t][j]);
        }
    }
    return res;
}
int main(){
    cin>>n>>m;
    memset(g,0x3f,sizeof g);
    while(m--){
        int a,b,c;
        scanf("%d%d%d", &a, &b ,&c);
        g[a][b]=g[b][a]=min(g[a][b],c);
    }
    int ans=prim();
    if(ans==INF) puts("impossible");
    else cout<<ans<<endl;
  return 0;
}
//  freopen("testdata.in", "r", stdin);

Kruskal算法求最小生成树

https://www.acwing.com/problem/content/861/
算法思路:
1.将边权重从小到大排序
2.枚举每条边a,b权重c
如果不连通就加入一个集合(并查集)

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
#include<cstring>
#include<unordered_map>
using namespace std;
typedef long long LL;
const int N=1e5+10,M=2e5+10,INF=0x3f3f3f3f;
int n,m;
int p[N];
struct Edge{
    int a,b,c;
}edge[M];
bool cmp(Edge x,Edge y){
    return x.c<y.c;
}
int find(int x){
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}
int kruskal(){
    for(int i=1;i<=n;i++) p[i]=i;
    int res=0,cnt=0;
    for(int i=1;i<=m;i++){
        int a=edge[i].a;
        int b=edge[i].b;
        int c=edge[i].c;
        a=find(a);
        b=find(b);
        if(a!=b){
            res+=c;
            p[a]=b;
            cnt++;
        }
    }
    if(cnt<n-1) return INF;
    else return res;
}
int main(){
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        int a,b,c;
        cin>>a>>b>>c;
        edge[i]={a,b,c};
    }
    sort(edge+1,edge+1+m,cmp);
    int t=kruskal();
    if (t==INF) puts("impossible");
    else printf("%d\n",t);

  return 0;
}
//  freopen("testdata.in", "r", stdin);
posted @ 2021-05-16 22:14  一个经常掉线的人  阅读(67)  评论(0)    收藏  举报