图论算法

存图

//邻接表
int h[N],w[N],e[N],ne[N],idx;
void add(int a,int b,int c){
    e[idx] = b,w[idx] = c,ne[idx] = h[a],h[a] = idx++;
}
for(int i = h[u]; i != 01; i = ne[i]){
	int j = e[i];
    int value = w[i];
}
//无边权
const int N=1e5+10;
vector<int> G[N];
void addedge(int u,int v){
	G[u].push_back(v);
	G[v].push_back(u);
}
//有边权
vector<pair<int,int> > G[N];
void addedge(int u,int v,int w){
	G[u].push_back(make_pair(v,w));
	G[v].push_back(make_pair(u,w));
}
int to = G[i].first(),w = G[i].second();

******************************************************************
//去重
//链式前向星存图 
struct node{
	int to,next,w;
}edges[maxn];
int cnt = 0,head[maxn],mp[maxn][maxn];
void add(int u,int v,int w){
	edges[++cnt].to = v;
	edges[cnt].next = head[u];
	edges[cnt].w = w;
	head[u] = cnt;
}
for(int i = 1,x,y,w; i <= m; i++){
	cin >> x >> y >> w;
	if(mp[x][y]){
		edges[mp[a][b]].w = min(edges[mp[a][b]].w,c);
	}else{
		add(a,b,c);
		mp[a][b] = cnt;
	}
}
//set去重用法和使用vector一样push_back()换成insert()
set<pair<int,int> > G[maxn];	//只能用迭代器进行遍历
for(int i = 1,x,y,z; i <= m; i++){
        	cin >> x >> y >> z;
	G[x].insert(make_pair(y,z));
}

BFS

//vector存储
#include <bits/stdcpp.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6+10,maxm = 2e6+10;
const int INF = 0x3f3f3f3f,mod = 100003;
vector<int> g[maxm];
int dep[maxn],vis[maxn],num[maxn];
int main(){
	int n,m; cin >> n >> m;
	for(int i = 1,x,y; i <= m; i++){
        cin >> x >> y;
        g[x].push_back(y);
        g[y].push_back(x);
	}
	dep[1] = 1,vis[1] = 1,num[1] = 1;
	queue<int> q;
	q.push(1);
	while(!q.empty()){
        int t = q.front();
        q.pop();
        for(int i = 0; i < (int)g[t].size(); i++){
            int x = g[t][i];
            if(!vis[x]){
                dep[x] = dep[t]+1;
                vis[x] = 1;
                q.push(x);
            }
            if(dep[x] == dep[t]+1){
                num[x] = (num[x]+num[t])%mod;
            }
        }
	}
	for(int i = 1; i <= n; i ++){
        cout << num[i] << endl;
	}
    return 0;
}

Dijkstra

//链式前向星存
#include <bits/stdcpp.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5+10;
const int INF = 0x3f3f3f3f;
struct node1{
    int num;	//编号
    int d;		//从起点到该点的最短路程
    bool operator < (const node1 &a) const {
        return d>a.d;//最小值优先
    }
};
struct node2{
	int to,w,next;
}edge[maxn];
int cnt = 0,head[maxn];
int n,m,s,dis[maxn],vis[maxn];
void add(int u,int v,int w){
	edge[++cnt].to = v;
	edge[cnt].w = w;
	edge[cnt].next = head[u];
	head[u] = cnt;
}
void Dijkstra(int s){
	for(int i = 1; i <= n; i++)
		dis[i] = INF;
	dis[s] = 0;
	priority_queue<node1> q;
	q.push((node1){s,dis[s]});
	while(!q.empty()){
		int u = q.top().num;
		q.pop();
		if(vis[u])continue;
		vis[u] = true;
		for(int i = head[u]; i ; i = edge[i].next){
			int t = edge[i].to;
			if(dis[u]+edge[i].w < dis[t]){
				dis[t] = dis[u]+edge[i].w;
				q.push((node1){t,dis[t]});
			}
		}
	}
}
int main(){
	cin >> n >> m >> s;
	for(int i = 1, a,b,c; i <= m; i++){
		cin >> a >> b >> c;
		add(a,b,c);
	}
	Dijkstra(s);
	for(int i = 1; i <= n; i++)cout << dis[i] << ' ';
    return 0;
}

弗洛伊德

//多源最短路
#include <bits/stdcpp.h>
using namespace std;
const int N =1e5+10;
const int INF = 1e9;
int n,m,k;
int g[210][210];
int main(){
    scanf("%d%d%d",&n,&m,&k);
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++)
            if(i==j)g[i][j] = 0;
            else g[i][j] = INF;
    for(int i = 1; i <= m; i++){
       int x,y,w; cin >> x >> y >> w;
       g[x][y] = min(g[x][y],w);
    }
    for(int k = 1; k <= n; k++)
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++)
                g[i][j] = min(g[i][j],g[i][k]+g[k][j]);
    while(k--){
        int a,b;scanf("%d%d",&a,&b);
        if(g[a][b] > INF/2)puts("impossible");
        else printf("%d\n",g[a][b]);
    }
    return 0;
}

Bellman_Ford

善于求解有边数限制的最短路求法

#include <bits/stdcpp.h>
using namespace std;
const int maxn=1e4+10;
int n,m,k,dis[510],last[510];	//last数组是存储上一次松弛的dis数组
struct Edge{
    int x,y,w;
}edges[maxn];
void bellman_ford(){
    memset(dis,0x3f,sizeof dis);
    dis[1] = 0;
    for(int i = 1; i <= k; i++){		//进行k次松弛,就相当于走k条边
        memcpy(last,dis,sizeof dis);
        for(int j = 1; j <= m; j++){
            auto e = edges[j];
            dis[e.y] = min(dis[e.y],last[e.x]+e.w);	//和dijkstra差不多
        }
    }
}
int main(){
    scanf("%d%d%d",&n,&m,&k);
    for(int i = 1; i <= m; i++){
        int a,b,c;scanf("%d%d%d",&a,&b,&c);
        edges[i] = {a,b,c};
    }
    bellman_ford();
    if(dis[n] > 0x3f3f3f3f / 2)puts("impossible");
    else printf("%d\n",dis[n]);
    return 0;
}

SPFA

//没有优化
#include <bits/stdcpp.h>
using namespace std;
const int N =1e5+10;
int h[N],w[N],e[N],ne[N],idx;
int n,m,dis[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 spfa(){
    memset(dis,0x3f,sizeof dis);
    dis[1] = 0,st[1] = true;
    queue<int> q;
    q.push(1);
    while(!q.empty()){
        int t = q.front();
        q.pop();
        st[t] = false;
        for(int i = h[t]; i != -1; i = ne[i]){
            int j = e[i];
            if(dis[j] > dis[t]+w[i]){
                dis[j] = dis[t]+w[i];
                if(!st[j]){
                    q.push(j);
                    st[j] = true;
                }
            }
        }
    }
}
int main(){
    memset(h,-1,sizeof h);
    scanf("%d%d",&n,&m);
    for(int i = 1; i <= m; i++){
        int a,b,c; scanf("%d%d%d",&a,&b,&c);
        add(a,b,c);
    }
    spfa();
    if(dis[n]==0x3f3f3f3f)puts("impossible");
    else printf("%d\n",dis[n]);
    return 0;
}
//被双端队列优化过的spfa
#include <bits/stdcpp.h>
using namespace std;
const int maxn=1e5+10,maxm=2e5+10,INF = 0x3f3f3f3f;
struct node{
	int to,w,next;
}edge[maxm];
int n,m,s,head[maxn],dis[maxn],cnt = 0,vis[maxn];
void add(int u,int v,int w){
    edge[++cnt].to = v,edge[cnt].w = w,edge[cnt].next = head[u],head[u] = cnt;
}
void spfa(){
	for(int i = 1; i <= n; i++){
        vis[i] = 0;
        dis[i] = INF;
	}
	deque<int> Q;
	Q.push_back(s);
	vis[s]=1;dis[s]=0;
	while(!Q.empty()){
		int t=Q.front();
		Q.pop_front();
		vis[t]=0;
		for(int i=head[t]; i ;i=edge[i].next){
			int u=edge[i].to;
			if(dis[u]>dis[t]+edge[i].w){
				dis[u]=dis[t]+edge[i].w;
				if(!vis[u]){
					if(!Q.empty()&&dis[u]<dis[Q.front()])Q.push_front(u);
					else Q.push_back(u);
					vis[u]=1;
				}
			}
		}
	}
}
int main(){
	scanf("%d%d%d",&n,&m,&s);
	for(int i = 0, u, v, w;i < m; i++){
		cin>>u>>v>>w;
		add(u,v,w);
	}
	spfa();
	for(int i = 1;i <= n;i++)
	cout<<dis[i]<<endl;
	return 0;
}

SPFA判断负环

#include <bits/stdcpp.h>
using namespace std;
const int N =1e5+10;
int h[N],w[N],e[N],ne[N],idx;
int n,m,dis[N],cnt[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++;
}
bool spfa(){
    queue<int> q;
    for(int i = 1; i <= n; i++){            //因为判断从每个点开始是否存在负环,不单单只是从一号点
        st[i] = true;
        q.push(i);
    }
    while(!q.empty()){
        int t = q.front();
        q.pop();st[t] = false;
        for(int i = h[t]; i!=-1; i = ne[i]){
            int j = e[i];
            if(dis[j] > dis[t] + w[i]){
                dis[j] = dis[t] + w[i];
                cnt[j] = cnt[t] + 1;
                if(cnt[j]>=n)return true;
                if(!st[j]){
                    q.push(j);
                    st[j] = true;
                }
            }
        }
    }
    return false;
}
int main(){
    memset(h,-1,sizeof h);
    scanf("%d%d",&n,&m);
    for(int i = 1; i <= m; i++){
        int a,b,c; scanf("%d%d%d",&a,&b,&c);
        add(a,b,c);
    }
    if(spfa())puts("Yes");
    else puts("No");
    return 0;
}

Prim

#include <bits/stdcpp.h>
using namespace std;
const int N = 510, INF = 0x3f3f3f3f,maxn = 2e5+10;
int n, m;
bool st[N];
int h[maxn],w[maxn],e[maxn],ne[maxn],idx;
struct node{
    int ip;
    int value;
    bool operator < (const node &a)const {
        return value > a.value;
    }
};
void add(int a,int b,int c){
    e[idx] = b,w[idx] = c,ne[idx] = h[a],h[a] = idx++;
}
int prim(){
    int res = 0,cnt = 0;
    priority_queue<node> q;
    q.push({1,0});
    while(q.size()){
        auto t = q.top();
        q.pop();
        if(st[t.ip]) continue;
        st[t.ip] = true,res += t.value,cnt++;
        for(int i = h[t.ip]; i != -1; i = ne[i]){
            int j = e[i];
            if(!st[j]){
                q.push({j,w[i]});
            }
        }
    }
    if(cnt != n)return INF;
    return res;
}
int main()
{
    scanf("%d%d", &n, &m);
    memset(h,-1,sizeof h);
    for(int i = 1; i <= m; i++){
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        add(a,b,c);
        add(b,a,c);
    }
    int t = prim();
    if(t==INF)puts("impossible");
    else printf("%d\n",t);
    return 0;
}

拓扑排序

#include <bits/stdcpp.h>
using namespace std;
const int maxn=1e5+10;
vector<int> G[maxn];
vector<int> g;
queue<int> q;
int num[maxn];//记录入度
int n,m;
int find(){
    int cnt = 0;
    for(int i=1; i<=n; i++){
        if(num[i]==0){
            g.push_back(i);
            q.push(i);
            cnt++;
        }
    }
    while(q.size()){
        int t = q.front();
        q.pop();
        for(auto u : G[t]){
            num[u]--;
            if(num[u]==0){
                g.push_back(u);
                cnt++;
                q.push(u);
            }
        }
    }
    if(cnt==n)return 1;
    else return 0;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i = 1; i <= m; i++){
        int x,y;scanf("%d%d",&x,&y);
        G[x].push_back(y);
        num[y]++;
    }
    if(!find())cout << -1 << endl;
    else {
        for(auto x:g)cout << x << ' ';
        cout << endl;
    }
    return 0;
}

并查集

普通写法:
int find(int a){
	int r = a;
	while(f[r] != r) 		//寻找根结点
		r = f[r];
	return r;
}
递归写法:
//递归形式的路径压缩
int find(int x){
    return f[x]==x?x:(f[x]=find(f[x]));
}
迭代写法:
//迭代形式的路径压缩
int find(int a){
    int p = a, t;
    while (f[p] != p) p = f[p];//跟递归不同的是,通过循环先找到祖先p,然后再接着改结点
    while (a!= p){ 
    	t = f[a];
    	f[a] = p;
    	 a = t; 
    }
    return a;
}
合并:
void memge(int x,int y){
	x = find(x);
	y = find(y);
	if(x!=y)f[y] = x;
}

带权值的并查集

#include <bits/stdcpp.h>
using namespace std;
const int N = 50010;
int n, m;
int p[N], d[N];			//p[x]表示关系,d[x]表示到根的权值
int find(int x) {
    if (p[x] != x) {
        int t = find(p[x]); //找根
        d[x] += d[p[x]];    //更新距离
        p[x] = t;           //指向根
    }
    return p[x];
}
int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i ++ ) p[i] = i;
    int res = 0;
    while (m -- ){
        int t, x, y;
        scanf("%d%d%d", &t, &x, &y);
        if (x > n || y > n) res ++ ;
        else{
            int px = find(x), py = find(y);
            if (t == 1) {                                       //x和y是同类
                if (px == py && (d[x] - d[y]) % 3) res ++ ;     //如果d[x]=d[y]说明距离相等
                else if (px != py) {                            //更新
                    p[px] = py;
                    d[px] = d[y] - d[x];                        //(d[x]+?-d[y])%3==0
                }
            }else {                                             //x和y不是同类
                if (px == py && (d[x] - d[y] - 1) % 3) res ++ ;
                else if (px != py) {
                    p[px] = py;
                    d[px] = d[y] + 1 - d[x];                    //(d[x]+?-d[y]-1)%3==0
                }
            }
        }
    }
    printf("%d\n", res);
    return 0;
}

全员最短路

1.不带负权边的
直接来n次优化过的Dijkstra求时间复杂度为O(n*m*log(n))
不能用Floyd,O(n^3)太慢了
2.带负权边的
单纯的Dijkstra已经不行了
用SPFA求出一个上帝视角的h[maxn]数组:从一个源点S到每一个点的最短路,且S到每个点已知距离为0
然后再求n此优化过的Dijkstra

#include<bits/stdcpp.h>
using namespace std;
typedef long long ll;
const ll INF  = 1e9;
const int maxn = 6e3 + 5;
struct node{
	ll dis, id;
	inline bool operator < (const node &x)const{
		return dis > x.dis;
	}
	node (int x, int y) { dis = x, id = y; }
};
int n, m, in[maxn],vis[maxn];
ll h[maxn], dis[maxn];
vector<pair<int, ll> > G[maxn];
bool SPFA(int s) { //从源点开始求一遍最短路 , 记录在h数组中
	queue <int> q;
	memset(h,0x3f,sizeof(h));
	h[s] = 0;
    vis[s] = 1;
    q.push(s);
	while(!q.empty()) {
		int u = q.front();
		q.pop();
		vis[u] = 0;
		for(int i=0; i<(int)G[u].size(); i++) {
			int v = G[u][i].first;
			ll w = G[u][i].second;
			if(h[v] > h[u]+w) {
				h[v] = h[u] + w;
				if(++in[v] >= n) return true;
				if(!vis[v]) {
					vis[v] = 1;
					q.push(v);
				}
			}
		}
	} return false;
}
void dijkstra(int s) {
	priority_queue<node> q;
	memset(vis, 0, sizeof(vis));
	for(int i=1; i<=n; i++) dis[i] = (i==s) ? 0 : (INF);
	q.push(node(0, s));
	while(!q.empty()) {
		int u = q.top().id;
		q.pop();
		if(vis[u])continue;
		vis[u] = 1;
		for(int i=0; i<(int)G[u].size(); i++) {
			int v = G[u][i].first;
			ll w = G[u][i].second;
			if(dis[v] > dis[u]+w) {
				dis[v] = dis[u] + w;
				if(!vis[v]) q.push(node(dis[v], v));
			}
		}
	}
}
int main() {
	scanf("%d%d", &n, &m);
	while(m--) {
		int u, v;
		ll w;
		scanf("%d%d%lld", &u, &v, &w);
		G[u].push_back(make_pair(v, w));
	}
	for(int i=1; i<=n; i++) G[0].push_back(make_pair(i, 0*1ll));
	if(SPFA(0)){
        printf("-1\n");
        return 0;
	}
	for(int u=1; u<=n; u++)
		for(int i=0; i<(int)G[u].size(); i++)
			G[u][i].second += h[u] - h[G[u][i].first];	//u -> v: W(u,v) = w(u,v)+h[u]-h[v];
	for(int i=1; i<=n; i++) {
		dijkstra(i); ll ans = 0;
		for(ll j=1; j<=n; j++)
			ans += (dis[j]==INF) ? (j*INF) : (j*(dis[j]+h[j]-h[i]));// u -> v: w(u,v) = W[u,v] + h[v]-h[u];与上面与之对应
		printf("%lld\n", ans);
	}
	return 0;
}

N皇后

#include<cstdio>
#include<iostream>
using namespace std;
int ans[14],check[3][28]={0},sum=0,n;
void dfs(int num){
    if(num>n){
        sum++;
        if(sum>3) return;
        else{
            for(int i=1;i<=n;i++) printf("%d ",ans[i]);
            printf("\n");
            return;
        }
    }
    for(int i=1;i<=n;i++){
        if((!check[0][i])&&(!check[1][num+i])&&(!check[2][num-i+n])){
            ans[num]=i;
            check[0][i]=1; check[1][num+i]=1; check[2][num-i+n]=1;
            dfs(num+1);
            check[0][i]=0; check[1][num+i]=0; check[2][num-i+n]=0;
        }
    }
}
int main(){
    scanf("%d",&n);
    dfs(1);
    printf("%d",sum);
    return 0;
}

二分图的判断

#include <bits/stdcpp.h>
using namespace std;
const int N = 2e5+10;       //数组要开二倍大小,因为是无向图
int n,m,st[N];
int h[N],e[N],ne[N],idx;    
void add(int a,int b){
    e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}
bool dfs(int x,int t){
    st[x] = t;
    for(int i = h[x]; i != -1; i = ne[i]){
        int j = e[i];
        if(!st[j]){
            if(!dfs(j,3-t))return false;    //没有被染色,那就去染色
        }else if(st[j]==t)return false;        //如果发生染色冲突,那就直接结束
    }
    return true;
}
int main()
{
    scanf("%d%d",&n,&m);
    memset(h,-1,sizeof h);
    for(int i = 1; i <= m; i++){
        int a,b;scanf("%d%d",&a,&b);
        add(a,b),add(b,a);
    }
    bool f = true;
    for(int i = 1; i <= n; i++){            //因为不能保证整个图都是连通的,所以都要遍历
        if(!st[i]){
            if(!dfs(i,1)){
                f = false;
                break;
            }
        }
    }
    if(f)puts("Yes");
    else puts("No");
    return 0;
}

二分图最大匹配(匈牙利算法)

#include <bits/stdcpp.h>
using namespace std;
const int N = 1e5+10;
int n1,n2,m,match[N];       //match数组是记录右边端点的匹配情况
bool st[N];                 //标记有哪些被访问过了
int h[N],e[N],ne[N],idx;
void add(int a,int b){
    e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}
bool find(int x){
    for(int i = h[x]; i != -1; i = ne[i]){
        int j = e[i];
        if(!st[j]){
            st[j] = true;
            if(match[j] == 0||find(match[j])){  //1.没有被匹配   2.被匹配了,但是被匹配的结点有其他选择
                match[j] = x;
                return true;
            }
        }
    }
    return false;       //没有一个匹配的
}
int main()
{
    scanf("%d%d%d",&n1,&n2,&m);
    memset(h,-1,sizeof h);
    for (int i = 1; i <= m; i++){
        int a,b;scanf("%d%d",&a,&b);
        add(a,b);
    }
    int res = 0;
    for(int i = 1; i <= n1; i++){   //从左边开始匹配
        memset(st,false,sizeof st);     //每次匹配都要尝试右边的每一个结点
        if(find(i))res++;
    }
    printf("%d",res);
    return 0;
}

树的直径

//无向无边权图
//法一:两次dfs,第一次求出最远点,第二次以最远点为起点,再次求出的最远点对应的就是树的直径
#include <bits/stdcpp.h>
using namespace std;
const int maxn = 1e4+10;
vector<int> g[maxn];
int dis[maxn],ans,pos;
void dfs(int x,int father){
    if(ans <= dis[x]){
        ans = dis[x];
        pos = x;
    }
    int le = g[x].size();
    for(int i = 0; i < le; i++){
        int to = g[x][i];
        if(to==father)continue;
        dis[to] = dis[x]+1;
        dfs(to,x);
    }
}
int main(){
    int n; cin >> n;
    int x,y;
    for(int i = 1; i < n; i++){
        cin >> x >> y;
        g[x].push_back(y);
        g[y].push_back(x);
    }
    ans = 0;
    dis[x] = 0;
    dfs(x,0);
    ans = 0;
    dis[pos] = 0;
    dfs(pos,0);
    cout << ans << endl;
    return 0;
}
//树形dp写法
#include <bits/stdcpp.h>
using namespace std;
const int maxn = 1e4+10;
vector<int> g[maxn];
int f1[maxn],f2[maxn],ans,n,m;
void dp(int x,int father){
	for(int i = 0,j; i < (int)g[x].size(); i++){
		j = g[x][i];
		if(j==father)
		  continue;
		dp(j,x);
		if(f1[x] < f1[j] + 1){   ///f1[j]表示j到叶子节点距离的最大值
			f2[x] = f1[x];          ///f2[x]表示x到叶子节点距离的次大值
			f1[x] = f1[j] + 1;
		}else if(f2[x] < f1[j] + 1) ///这个就是判断节点x到次远点的更新
		  f2[x] = f1[j] + 1;
		ans=max(ans,f1[x] + f2[x]);
	}
}
int main(){
	scanf("%d",&n);
	for(int i = 1,x,y;i < n; i++){
		scanf("%d%d",&x,&y);
		g[x].push_back(y);
		g[y].push_back(x);
	}
	dp(1,0);
	printf("%d",ans);
	return 0;
}
//对于无向有权图,两遍bfs,和dfs一样的原理
#include <bits/stdcpp.h>
using namespace std;
const int N = 1e5+10;
vector<pair<int,int> > G[N];
int n,pos,vis[N],ans;
struct node{
    int d;
    int step;
};
void addedge(int u,int v,int w){
	G[u].push_back(make_pair(v,w));
	G[v].push_back(make_pair(u,w));
}
void bfs(int s){
    queue<node> q;
    q.push((node){s,0});
    int ma = -1;
    while(!q.empty()){
        node t = q.front();
        q.pop();
        for(int v = 0; v < (int)G[t.d].size(); v++){
            int to = G[t.d][v].first;
            if(vis[to])continue;
            vis[to] = 1;
            int w = G[t.d][v].second;
            if(w+t.step>ma){
                ma = w+t.step;
                pos = to;
            }
            q.push((node){to,w+t.step});
        }
    }
    ans=ma;
}
int main(){
    cin >> n;
    for(int i = 1; i < n; i++){
        int x,y,t;cin >> x >> y >> t;
        addedge(x,y,t) ;
    }
    vis[1] = 1;
    bfs(1);
    memset(vis,0,sizeof vis);
    vis[pos] = 1;
    bfs(pos);
    cout << ans << endl;
    return 0;
}

树的重心

//树的重心:以某个点为根,它的最大的子树最小
#include <bits/stdcpp.h>
using namespace std;
const int maxn = 1e5+10;
int n,father[maxn],sz[maxn],ans,num,res;
vector<int> g[maxn];
int dfs(int u,int fa){						//给每个点附上权值
    father[u] = fa;
    sz[u] = 1;
    for(int v = 0; v < g[u].size(); ++v){
        if(g[u][v]!=fa)
            sz[u]+=dfs(g[u][v],u);
    }
    return sz[u];
}
void dfs_n(int u,int fa,int step){			//从重心到每个节点的距离之和
    for(int v = 0; v < g[u].size(); ++v){
        if(g[u][v]!=fa){
            dfs_n(g[u][v],u,step+1);
        }
    }
    num+=step;
}
int main(){
    cin >> n;
    for(int i = 1,x,y; i < n; i++){
        cin >> x >> y;
        g[x].push_back(y);
        g[y].push_back(x);
    }
    dfs(1,0);
    int mi = maxn+10;       //mi存的是最大值的最小值
    for(int u = 1; u <= n; ++u){
        int ma = n - sz[u];     //ma以每个点为重心的最大子树值,n - sz[u]是指父亲那棵子树的值也要考虑到
        int f;
        for(int v = 0; v < g[u].size(); ++v){
            if(g[u][v]!=father[u])
                ma = max(ma,sz[g[u][v]]);
        }
        if(ma < mi){		//更新
            mi = ma;
            ans = u;
            res = ma;
        }
    }
    cout << res << endl;
    //dfs_n(ans,0,0);
    //cout << ans << ' ' << num << endl;
    return 0;
}

树上最远距离

//求树上每个点能到达的最远距离,hdu2196
/*两次dfs
第一次记录从下向上最长距离
第二次记录从父节点到儿子节点最长距离*/
#include <bits/stdcpp.h>
using namespace std;
const int maxn = 1e4+10;
vector<pair<int,int> > G[maxn];
int f[maxn],g[maxn],p[maxn];
void init(int n){
    memset(f,0,sizeof(f));
    memset(g,0,sizeof(g));
    memset(p,0,sizeof(p));
    for(int i = 1; i <= n; ++i)G[i].clear();
}
int dfs1(int u,int pre){
    for(int i = 0; i < (int)G[u].size(); ++ i){
        if(G[u][i].first!=pre){
            f[u] = max(f[u],G[u][i].second+dfs1(G[u][i].first,p[G[u][i].first] = u));
        }
    }
    return f[u];
}
void dfs2(int u,int pre){
    int t = 0;
    g[u] = g[pre];
    for(int i = 0; i < (int)G[pre].size(); ++ i){
        if(G[pre][i].first == p[pre])continue;
        if(G[pre][i].first == u)t = G[pre][i].second;
        else g[u] = max(g[u],f[G[pre][i].first]+G[pre][i].second);
    }
    g[u]+=t;
    for(int i = 0; i < (int)G[u].size(); ++ i){
        if(G[u][i].first != pre)dfs2(G[u][i].first,u);
    }
}
int main(){
	int n;
	while(~scanf("%d",&n)){
        init(n);
        for(int i = 2,x,y; i <= n; ++i){
            cin >> x >> y;
            G[i].push_back(make_pair(x,y));
            G[x].push_back(make_pair(i,y));
        }
        dfs1(1,0);
        dfs2(1,0);
        for(int i = 1; i <= n; i++){
            cout << max(f[i],g[i]) << endl;
        }
	}
	return 0;
}

分层图最短路

#include <bits/stdcpp.h>
#define ll long long
#define inf 0x7f7f7f7f
const int maxn = 5e5+10;
using namespace std;
struct node{
    int to,w,next;
}edge[maxn];
struct Node{
    int dis,id;
    Node(const int a,const int b){
        dis = a;
        id = b;
    }
    bool operator < (const Node& a)const {
        return dis > a.dis;
    }
};
int head[maxn], cnt;
int dis[maxn], vis[maxn];
int n, m, t, k;
void init(){
    memset(head,0,sizeof(head));
    memset(dis,0x3f,sizeof(dis));
    memset(vis,0,sizeof(vis));
    cnt = 0;
}
void add(int u,int v,int w){
    edge[++cnt].to = v;
    edge[cnt].w = w;
    edge[cnt].next = head[u];
    head[u] = cnt;
}
void dijkstra(){
    priority_queue<Node> q;
    dis[1] = 0; q.push(Node(dis[1],1));
    while(!q.empty()){
        int now = q.top().id;
        q.pop();
        if(vis[now]) continue;
        vis[now] = 1;
        for(int i = head[now]; i ; i = edge[i].next){
            int v = edge[i].to;
            if(!vis[v]&&dis[v] > dis[now] + edge[i].w){
                dis[v] = dis[now] + edge[i].w;
                q.push(Node(dis[v],v));
            }
        }
    }
}
int main(){
    while(~scanf("%d%d%d", &n, &m, &k)){
        init();
        while(m--){
            int u, v, w;
            scanf("%d%d%d",&u, &v, &w);
            for(int i = 0; i <= k; i++){
                add(u + i * n, v + i * n, w);
                add(v + i * n, u + i * n, w);
                if(i != k){
                    add(u + i * n, v + (i + 1) * n, w*0.5);
                    add(v + i * n, u + (i + 1) * n, w*0.5);
                }
            }
        }
        dijkstra();
        int ans = inf;
        for(int i = 0; i <= k; i++)
            ans = min(ans, dis[n + i * n]);
        printf("%d\n",ans);
    }
	return 0;
}

最短路径数

#include <bits/stdcpp.h>
using namespace std;
typedef long long ll;
const int maxn = 2e3+10;
const int INF = 0x3f3f3f3f;
set<pair<int,int> > G[maxn];
ll dis[maxn],vis[maxn],cnt[maxn];
struct node{
    int num;	//编号
    int d;		//从起点到该点的最短路程
    bool operator < (const node &a) const {
        return d>a.d;
    }
};
int main(){
    int n,m; cin >> n >> m;
    for(int i = 1,x,y,z; i <= m; i++){
        cin >> x >> y >> z;
        G[x].insert(make_pair(y,z));
    }
    for(int i = 1; i <= n; i ++)dis[i] = INF;
    dis[1] = 0,cnt[1] = 1;
    priority_queue<node> q;
    q.push((node){1,0});
    while(!q.empty()){
        int t = q.top().num;
        q.pop();
        if(vis[t])continue;
		vis[t] = 1;
        for(auto i = G[t].begin(); i!=G[t].end(); i ++){
            int x = i->first;
            int w = i->second;
			if(dis[x]==dis[t]+w){
                cnt[x] += cnt[t];
            }else if(dis[t]+w<dis[x]){
                dis[x] = dis[t]+w;
                cnt[x] = cnt[t];
                q.push((node){x,dis[x]});
            }

        }
    }
    if(dis[n]==INF){
        cout << "No answer" << endl;
        return 0;
    }
    cout << dis[n] << ' ' << cnt[n] << endl;
    return 0;
}

最小覆盖圆

#include <iostream>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
struct Point{
    double x,y;
};
struct Point a[1005],d;
double r;
double  get_dis(Point p1,Point  p2){	//两点间距离
    return (sqrt((p1.x-p2.x)*(p1.x -p2.x)+(p1.y-p2.y)*(p1.y-p2.y)));
}
double get_muti(Point p1, Point p2,Point p0){
    return   ((p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y));
}
void get_o(Point p,Point q,int n){
    d.x=(p.x+q.x)/2.0;
    d.y=(p.y+q.y)/2.0;
    r=get_dis(p,q)/2;
    int k;
    double c1,c2,t1,t2,t3;
    for(k=1;k<=n;k++) {
        if(get_dis(d,a[k])<=r)continue;
        if(get_muti(p,q,a[k])!=0.0) {
            c1=(p.x*p.x+p.y*p.y-q.x*q.x-q.y*q.y)/2.0;
            c2=(p.x*p.x+p.y*p.y-a[k].x*a[k].x-a[k].y*a[k].y)/2.0;
            d.x=(c1*(p.y-a[k].y)-c2*(p.y-q.y))/((p.x-q.x)*(p.y-a[k].y)-(p.x-a[k].x)*(p.y-q.y));
            d.y=(c1*(p.x-a[k].x)-c2*(p.x-q.x))/((p.y-q.y)*(p.x-a[k].x)-(p.y-a[k].y)*(p.x-q.x));
            r=get_dis(d,a[k]);
        }
        else {
            t1=get_dis(p,q);
            t2=get_dis(q,a[k]);
            t3=get_dis(p,a[k]);
            if(t1>=t2&&t1>=t3) {
                d.x=(p.x+q.x)/2.0;
                d.y=(p.y+q.y)/2.0;r=get_dis(p,q)/2.0;
            }else if(t2>=t1&&t2>=t3) {
                d.x=(a[k].x+q.x)/2.0;
                 d.y=(a[k].y+q.y)/2.0;
                 r=get_dis(a[k],q)/2.0;
            }else {
                d.x=(a[k].x+p.x)/2.0;
                d.y=(a[k].y+p.y)/2.0;
                r=get_dis(a[k],p)/2.0;
            }
        }
    }
}
void solve(Point pi,int n){
    d.x=(pi.x+a[1].x)/2.0;
    d.y=(pi.y+a[1].y)/2.0;
    r=get_dis(pi,a[1])/2.0;
    int j;
    for(j=2;j<=n;j++){
    if(get_dis(d,a[j])<=r)continue;
    else get_o(pi,a[j],j-1);
    }
}
int main(){
    int i,n;
    while(scanf("%d",&n)&&n){
        for(i=1;i<=n;i++){
            scanf("%lf %lf",&a[i].x,&a[i].y);
        }
	random_shuffle(a+1,a+n+1);
        if(n==1){ printf("%.2lf %.2lf 0.00\n",a[1].x,a[1].y);continue;}
        r=get_dis(a[1],a[2])/2.0;
        d.x=(a[1].x+a[2].x)/2.0;
        d.y=(a[1].y+a[2].y)/2.0;
        for(i=3;i<=n;i++){
            if(get_dis(d,a[i])<=r)continue;
            else
            solve(a[i],i-1);
        }
        printf("%.2lf %.2lf %.2lf\n",d.x,d.y,r);
    }
    return 0;
}

难点算法

严格次小生成树

#include <bits/stdcpp.h>
using namespace std;
const int N=1e5+10;
//前向星存边
struct Edge{
    int to,from,next,val;
    bool isin;
    Edge(){
		isin=val=next=0;
	}
    bool operator < (const Edge &A)const {
        return val<A.val;
    }
}e[N<<1],E[N*3];
int len,Head[N];
void Ins(int a,int b,int c){
    e[++len].to=b;e[len].val=c;
    e[len].next=Head[a];Head[a]=len;
}
//并查集+Kurskal
int f[N],m,n;
int find(int x){
    return f[x]==x?x:(f[x]=find(f[x]));
}
int ans=0x3f3f3f3f;long long tot=0;
void Krs(){
    int cnt=1;
    sort(E+1,E+m+1);
    for(int i=1;cnt<n;i++){
        int v=E[i].to,u=E[i].from;
        if(find(v)!=find(u)){
            cnt++;
            E[i].isin=1;
            tot+=E[i].val;//计算最小生成树
            Ins(u,v,E[i].val);
            Ins(v,u,E[i].val);
            f[find(v)]=find(u);
        }
    }
}
//倍增lca板子
int dep[N],p[N][20],Max[N][20],Smax[N][20];
void dfs(int x){
    for(int i=0;p[x][i];i++){
        p[x][i+1]=p[p[x][i]][i];
        Max[x][i+1]=max(Max[x][i],Max[p[x][i]][i]);
        //注意求次大的时候看看两段的最大值是不是相等,如果不判断的话
        //在最大值相等的时候,次大值会被更新为最大值
        if(Max[x][i]==Max[p[x][i]][i])
            Smax[x][i+1]=max(Smax[x][i],Smax[p[x][i]][i]);
        else
            Smax[x][i+1]=max(min(Max[x][i],Max[p[x][i]][i]),
                            max(Smax[x][i],Smax[p[x][i]][i]));
    }
    for(int i=Head[x];i;i=e[i].next){
        int v=e[i].to;
        if(v!=p[x][0]){
            dep[v]=dep[x]+1;
            Max[v][0]=e[i].val;
            Smax[v][0]=-1;
            p[v][0]=x;
            dfs(v);
        }
    }
}
//这段和lca板子一模一样
int lca(int a,int b){
    if(dep[a]<dep[b])swap(a,b);
    int d=dep[a]-dep[b];
    for(int i=0;d;i++,d>>=1)
        if(d&1)a=p[a][i];
    if(a==b)return a;
    for(int i=18;i>=0;i--)
        if(p[a][i]!=p[b][i])
            a=p[a][i],b=p[b][i];
    return p[a][0];
}
//计算
void calc(int u,int v,int w){
    int d=dep[u]-dep[v];//深度差,判断路径
    int m1=0,m2=0;
    for(int i=0;d;i++,d>>=1){
        if(d&1){//如果可以往上跳
            m2=max(m2,Smax[u][i]);//细节,先求次大值
            if(Max[u][i]>m1){//如果更新最大值,那么原来的最大值m1可能为新的次大值
                m2=max(m2,m1);//判断次大值是否更新
                m1=Max[u][i];//更新最大值
            }
        }
    }
    if(m1==w)ans=min(ans,w-m2);//如果最大值与边相等,用次大值更新
    else ans=min(ans,w-m1);//否则用最大值
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        f[i]=i;
    for(int i=1;i<=m;i++)
        scanf("%d%d%d",&E[i].from,&E[i].to,&E[i].val);
    Krs();
    dfs(1);
    for(int i=1;i<=m;i++){
        if(!E[i].isin){
            int u=E[i].from,v=E[i].to,Lca;
            Lca=lca(u,v);
            calc(u,Lca,E[i].val);
            calc(v,Lca,E[i].val);
        }
    }
    printf("%lld\n",tot+ans);
}

LCA

#include <bits/stdcpp.h>
using namespace std;
const int N=1e5+10;
//前向星存边
struct Edge{
    int to,from,next,val;
    bool isin;
    Edge(){
		isin=val=next=0;
	}
    bool operator < (const Edge &A)const {
        return val<A.val;
    }
}e[N<<1],E[N*3];
int len,Head[N];
void Ins(int a,int b,int c){
    e[++len].to=b;e[len].val=c;
    e[len].next=Head[a];Head[a]=len;
}
//并查集+Kurskal
int f[N],m,n;
int find(int x){
    return f[x]==x?x:(f[x]=find(f[x]));
}
int ans=0x3f3f3f3f;long long tot=0;
void Krs(){
    int cnt=1;
    sort(E+1,E+m+1);
    for(int i=1;cnt<n;i++){
        int v=E[i].to,u=E[i].from;
        if(find(v)!=find(u)){
            cnt++;
            E[i].isin=1;
            tot+=E[i].val;//计算最小生成树
            Ins(u,v,E[i].val);
            Ins(v,u,E[i].val);
            f[find(v)]=find(u);
        }
    }
}
//倍增lca板子
int dep[N],p[N][20],Max[N][20],Smax[N][20];
void dfs(int x){
    for(int i=0;p[x][i];i++){
        p[x][i+1]=p[p[x][i]][i];
        Max[x][i+1]=max(Max[x][i],Max[p[x][i]][i]);
        //注意求次大的时候看看两段的最大值是不是相等,如果不判断的话
        //在最大值相等的时候,次大值会被更新为最大值
        if(Max[x][i]==Max[p[x][i]][i])
            Smax[x][i+1]=max(Smax[x][i],Smax[p[x][i]][i]);
        else
            Smax[x][i+1]=max(min(Max[x][i],Max[p[x][i]][i]),
                            max(Smax[x][i],Smax[p[x][i]][i]));
    }
    for(int i=Head[x];i;i=e[i].next){
        int v=e[i].to;
        if(v!=p[x][0]){
            dep[v]=dep[x]+1;
            Max[v][0]=e[i].val;
            Smax[v][0]=-1;
            p[v][0]=x;
            dfs(v);
        }
    }
}
//这段和lca板子一模一样
int lca(int a,int b){
    if(dep[a]<dep[b])swap(a,b);
    int d=dep[a]-dep[b];
    for(int i=0;d;i++,d>>=1)
        if(d&1)a=p[a][i];
    if(a==b)return a;
    for(int i=18;i>=0;i--)
        if(p[a][i]!=p[b][i])
            a=p[a][i],b=p[b][i];
    return p[a][0];
}
//计算
void calc(int u,int v,int w){
    int d=dep[u]-dep[v];//深度差,判断路径
    int m1=0,m2=0;
    for(int i=0;d;i++,d>>=1){
        if(d&1){//如果可以往上跳
            m2=max(m2,Smax[u][i]);//细节,先求次大值
            if(Max[u][i]>m1){//如果更新最大值,那么原来的最大值m1可能为新的次大值
                m2=max(m2,m1);//判断次大值是否更新
                m1=Max[u][i];//更新最大值
            }
        }
    }
    if(m1==w)ans=min(ans,w-m2);//如果最大值与边相等,用次大值更新
    else ans=min(ans,w-m1);//否则用最大值
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        f[i]=i;
    for(int i=1;i<=m;i++)
        scanf("%d%d%d",&E[i].from,&E[i].to,&E[i].val);
    Krs();
    dfs(1);
    for(int i=1;i<=m;i++){
        if(!E[i].isin){
            int u=E[i].from,v=E[i].to,Lca;
            Lca=lca(u,v);
            calc(u,Lca,E[i].val);
            calc(v,Lca,E[i].val);
        }
    }
    printf("%lld\n",tot+ans);
}

Tarjan

#include <bits/stdcpp.h>
typedef long long ll;
const int maxn=2000+10;
const int maxm=100000+10;
const int INF = 0x3f3f3f3f;
using namespace std;
vector<int>g[1010];
 int n,m;
int scc_cnt,in[maxn],scc_be[maxn],dfn[maxn],low[maxn],cnt,S[maxn],top,isin[maxn];
void tarjan(int u){
    dfn[u]=low[u]=++cnt;
    S[++top]=u;
    isin[u]=1;						//判断u是否在栈中  
    for(int i=0;i<g[u].size();i++){
        int v=g[u][i];
        if(!dfn[v]){
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(isin[v]) low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u]){
        scc_cnt++;
        while(1){
            int x=S[top--];
            scc_be[x]=scc_cnt;
            isin[x]=0;
            if(x==u) break;
        }
    }
}
void find_scc(){
    memset(dfn,0,sizeof(dfn));
    memset(in,0,sizeof(in));
    memset(low,0,sizeof(low));
    top=-1;
    scc_cnt=0;
    for(int i=1;i<=n;i++)
        if(!dfn[i]) tarjan(i);
}
int main(){
    while(~scanf("%d%d",&n,&m)){
        int w[1010];
        cnt = 0;
        for(int i=1;i<=n;i++){
            scanf("%d",&w[i]);
            g[i].clear();
        }
        int u,v;
        for(int i=0;i<m;i++){
            scanf("%d%d",&u,&v);
            g[u].push_back(v);
        }
        find_scc();
        for(int i=1;i<=n;i++){
            for(int j=0;j<g[i].size();j++){
                int v=g[i][j];
                if(scc_be[v]!=scc_be[i])
                	in[scc_be[v]]=1;
                    
            }
        }
        int res=0,num=0;
        for(int i=1;i<=scc_cnt;i++){ 
            if(!in[i]){
                num++;
                int mi=INF;
                for(int j=1;j<=n;j++){
                    if(scc_be[j]==i)
                        mi=min(mi,w[j]);
                }
                res+=mi;
            }
        }
        cout << endl;
        printf("%d %d\n",num,res);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值