ACM寒假培训7--图与树

学习总结

最短路问题

一.Floyd算法

1. 不可以直接到达的点设为正无穷

2. 自己到自己的距离设为0

3. d [k] [i] [j] 为前k个点中i到j的最短路 

降维代码实现

const int N=105;
int d[N][N],n;
void floyd(){
	for(int k=1;k<=n;k++){
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
			}
		}
	}
}

二.Dijkstra算法

1.将顶点化为两堆,起初第一堆只有起点S一个点

2.每次从第二堆中距离S最近的点取出放入第一堆中,并更新最短路,直到第二堆没有点为止

3.维护出的dis[i]就是S到i点的最小距离了

代码实现

//不去重边版 
#include 
using namespace std;
const int INF = numeric_limits::max();

struct Edge {
    int to;
    int weight;
    Edge(int t, int w) : to(t), weight(w) {}
};

void dijkstra(int start, int n, vector>& graph, vector& dist, vector& count) {
    dist.assign(n, INF);
    count.assign(n, 0);
    dist[start] = 0;
    count[start] = 1;
    priority_queue, vector>, greater>> pq;
    pq.push(make_pair(0, start));
    while (!pq.empty()) {
        int u = pq.top().second;
        int d = pq.top().first;
        pq.pop();
        if (d > dist[u]) {
            continue;
        }
        for (int i = 0; i < graph[u].size(); ++i) {
            int v = graph[u][i].to;
            int w = graph[u][i].weight;
            if (dist[v] > dist[u] + w) {
                dist[v] = dist[u] + w;
                count[v] = count[u];
                pq.push(make_pair(dist[v], v));
            } else if (dist[v] == dist[u] + w) {
                count[v] += count[u];
            }
        }
    }
}

int main() {
    int n, m;
    cin >> n >> m;
    int start;  // 起点
    cin >> start;
    start--;
    vector> graph(n);
    for (int i = 0; i < m; ++i) {
        int u, v, w;
        cin >> u >> v >> w;
        graph[u - 1].push_back(Edge(v - 1, w));
    }
    
    vector dist;
    vector count;
    dijkstra(start, n, graph, dist, count);
    
    /*for (int i = 0; i < n; ++i) {
        cout << "到节点 " << i + 1 << " 的最短距离: ";
        if (dist[i] == INF) {
            cout << "INF";
        } else {
            cout << dist[i];
        }
        cout << ", 最短路数量: " << count[i] << endl;
    }*/
    
    for(int i=0;i

三.分层图最短路

应用情境: 在一个正常的图上可以进行k次操作,每次操作只影响目前的状态

1.采用离散化建图

2.有n个点时,1~n表示第一层, (1+i*n)~(n+i*n)表示第i+1层

3.在输入时对每层图间进行连接

代码实现

#include 
#define N 10000 
#define M 50000
#define K 10 
using namespace std;
struct Edge {
    int to,ne,w;
} edges[4000000];
int n,m,k,startt,endd;
int h[4000000], idx=0;
int dist[4000000];
bool st[4000000];
void add(int u, int v, int w) {
    idx++;
    edges[idx].to = v;
    edges[idx].ne = h[u];
    edges[idx].w = w;
    h[u] = idx;
}
void dijkstra() {
    priority_queue, vector>, greater > > q;
    q.push(make_pair(0,startt));
    dist[startt] = 0;
    while (!q.empty()) {
        int id = q.top().second;
        int dis = q.top().first;
        q.pop();
        if (st[id]) continue;
        st[id] = true;
        for (int i = h[id]; ~i; i = edges[i].ne) {
            int j = edges[i].to;
            if (dis + edges[i].w < dist[j]) {
                dist[j] = dis + edges[i].w;
                q.push(make_pair(dist[j], j));
            }
        }
    }
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    memset(h,-1,sizeof h);
    memset(dist,0x3f,sizeof dist);
    cin>>n>>m>>k>>startt>>endd;
    while(m--){
        int a,b,c;
        cin>>a>>b>>c;
        for(int i=0;i<=k;i++){
            add(a+i*n,b+i*n,c);
            add(b+i*n,a+i*n,c);
            if(i

树形dp问题

一.最大独立集问题

在一个无向图 G=(V, E)中,独立集是顶点集合 (V) 的一个子集 (S) ,使得 (S) 中任意两个顶点在图中都不相邻,即对于任意的 (u, v in S) ,都不存在边 ((u, v) in E) 。而最大独立集就是所有独立集中包含顶点数量最多的那个独立集。

#include 
using namespace std;
const int MAXN = 10005;
vector graph[MAXN];
bool k[MAXN];
int dp[MAXN][2];
void dfs(int u) {
    for (int v: graph[u]) {
            dfs(v);
            dp[u][1] += dp[v][0];
            dp[u][0] += max(dp[v][0], dp[v][1]);
    }
}
int main() {
    int n,root;
    cin >> n;
    for(int i=1;i<=n;i++) cin>>dp[i][1];
    for (int i = 1; i > u >> v;
        graph[v].push_back(u);
        k[u]=true;
    }
    for(int i=1;i<=n;i++){
    	if(!k[i]){
    		root=i;
    		break;
		}
	}
    dfs(root);
    cout << max(dp[root][0], dp[root][1]) << endl;
    return 0;
}

二.树的最小点覆盖问题

在一棵树中,点覆盖是一个顶点集合,使得树中的每一条边都至少有一个端点在这个集合中。最小点覆盖问题就是要找出这样一个点覆盖集合,其包含的顶点数量最少。

dp[u][0]=0,dp[u][1]=1;
dp[u][0]+=dp[v][1];
dp[u][1]+=min(dp[v][0], dp[v][1]);
cout<

三.树的最小支配集问题

对于一个无向图 G=(V, E),其中 V 是顶点集,E 是边集。如果存在一个顶点子集 (D subseteq V),使得对于图中任意一个顶点 (v in V),要么(v in D),要么 v与 D中的某个顶点相邻,那么称 D 是图 G 的一个支配集。

  • 最小支配集:在图 G 的所有支配集中,顶点个数最少的支配集称为最小支配集,其顶点个数称为最小支配数,记为 (gamma(G))

代码实现

#include 
using namespace std;
const int MAXN = 20005;
vector graph[MAXN];
bool vis[MAXN],over[MAXN];
int ans;
void dfs(int u,int fa) {
    vis[u]=1;
	bool f=0;
    for (int v: graph[u]){
    	if(v==fa) continue;
        dfs(v,u), f=f||over[v];
    }
    if(!f&&!over[u]&&!over[fa]) over[fa]=1,ans+=1;
}
int main() {
    int n;
    cin >> n;
    for (int i = 1; i >u>>v;
        graph[v].push_back(u);
        graph[u].push_back(v);
    }
    dfs(1,1);
    cout<

解题思路及代码

洛谷--P2015

  1. 这是一个树上背包问题
  2. 状态转移方程 : dp[u][i]=max(dp[u][i],dp[u][j]+dp[v][i-j-1]+app[u][l]);

其中dp[u][i]表示以u为根的子树保留i个树枝时得到的最大苹果数量

     3.给的图是无向的

     4. Dp外层从大到小遍历(即k~1),内层从小到大

AC代码

#include 
using namespace std;
const int maxn= 105;
int k,n,m;
vector graph[maxn],app[maxn];
int dp[maxn][maxn];
void dfs(int u,int fa) {
    for (int l=0;l=0;i--){
            	for(int j=0;j>n>>k;
    for (int i = 1; i >u>>v>>w;
        graph[v].push_back(u);
        app[v].push_back(w);
        graph[u].push_back(v);
        app[u].push_back(w);
    }
    dfs(1,0);
    cout<

洛谷--P4568

  1. 这是个分层图模板题
  2. 采用离散建图
  3. 注意开空间的大小,太大会爆
  4. 在建图时,只需在读入时连边就行

AC代码

#include 
#define N 10000 
#define M 50000
#define K 10 
using namespace std;
struct Edge {
    int to,ne,w;
} edges[4000000];
int n,m,k,startt,endd;
int h[4000000], idx=0;
int dist[4000000];
bool st[4000000];
void add(int u, int v, int w) {
    idx++;
    edges[idx].to = v;
    edges[idx].ne = h[u];
    edges[idx].w = w;
    h[u] = idx;
}
void dijkstra() {
    priority_queue, vector>, greater > > q;
    q.push(make_pair(0,startt));
    dist[startt] = 0;
    while (!q.empty()) {
        int id = q.top().second;
        int dis = q.top().first;
        q.pop();
        if (st[id]) continue;
        st[id] = true;
        for (int i = h[id]; ~i; i = edges[i].ne) {
            int j = edges[i].to;
            if (dis + edges[i].w < dist[j]) {
                dist[j] = dis + edges[i].w;
                q.push(make_pair(dist[j], j));
            }
        }
    }
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    memset(h,-1,sizeof h);
    memset(dist,0x3f,sizeof dist);
    cin>>n>>m>>k>>startt>>endd;
    while(m--){
        int a,b,c;
        cin>>a>>b>>c;
        for(int i=0;i<=k;i++){
            add(a+i*n,b+i*n,c);
            add(b+i*n,a+i*n,c);
            if(i

洛谷--P2016

  1. 这是个树的最小点覆盖问题
  2. Dp[i][0]表示不在i号点放士兵并以i为根的每条边都被看住的最小士兵数,Dp[i][1]表示在i号点放士兵并以i为根的每条边都被看住的最小士兵数
  3. 转移方程 : dp[u][0] += dp[v][1];  

dp[u][1] += min(dp[v][0], dp[v][1]);

AC代码

#include 
using namespace std;
const int MAXN = 10005;
vector graph[MAXN];
bool k[MAXN];
int dp[MAXN][2];
void dfs(int u) {
    for (int v: graph[u]) {
            dfs(v);
            dp[u][0] += dp[v][1];
            dp[u][1] += min(dp[v][0], dp[v][1]);
    }
}
int main() {
    int n,root;
    cin >> n;
    for(int i=0;i> u >> m;
        for(int j=1;j<=m;j++){
        	cin>>v;
        	graph[u].push_back(v);
        	k[v]=true;
		}
    }
    for(int i=0;i

HDU--1535

  1. 最短路+建反图
  2. 将求1到所有点的最短路和所有点到1的最短路通过反图转化成正反图中1到所有点的最短路

AC代码

#include
#include
#include
#include 
using namespace std;
const int INF = numeric_limits::max();
struct Edge {
    int to;
    int weight;
    Edge(int t, int w) : to(t), weight(w) {}
};
void dijkstra(int start, int n, vector>& graph, vector& dist, vector& count) {
    dist.assign(n, INF);
    count.assign(n, 0);
    dist[start] = 0;
    count[start] = 1;
    priority_queue, vector>, greater>> pq;
    pq.push(make_pair(0, start));
    while (!pq.empty()) {
        int u = pq.top().second;
        int d = pq.top().first;
        pq.pop();
        if (d > dist[u]) {
            continue;
        }
        for (int i = 0; i < graph[u].size(); ++i) {
            int v = graph[u][i].to;
            int w = graph[u][i].weight;
            if (dist[v] > dist[u] + w) {
                dist[v] = dist[u] + w;
                count[v] = count[u];
                pq.push(make_pair(dist[v], v));
            } else if (dist[v] == dist[u] + w) {
                count[v] += count[u];
            }
        }
    }
}
int main() {
	ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int n, m,k;
    cin>>k;
    for(int l=1;l<=k;l++){
    	cin >> n >> m;
    vector> graph(n);
    vector> graph1(n);
    for (int i = 0; i < m; ++i) {
        int u, v, w;
        cin >> u >> v >> w;
        graph[u - 1].push_back(Edge(v - 1, w));
        graph1[v - 1].push_back(Edge(u - 1, w));
    }
    vector dist;
    vector count;
    vector dist1;
    vector count1;
    dijkstra(0, n, graph, dist, count);
    dijkstra(0, n, graph1, dist1, count1);
    long long ans=0;
    for(int i=1;i

51Nod--2602

  1. 经典树的直径问题
  2. 两次dfs ,第一次找距任意一个点P最远的一个点Q ,第二次找距Q最远的点K, QK间的距离即为树的直径

AC代码

#include 
using namespace std;
const int maxn = 1e6+5;
vector graph[maxn];
int d[maxn],maxx;
void dfs(int u,int fa) {
    for (int v: graph[u]) {
            if(v==fa) continue;
            d[v]=d[u]+1;
            if(d[v]>d[maxx]) maxx=v;
            dfs(v,u);
    }
}
int main() {
	ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int n;
    cin>>n;
    for (int i=1;i>u>>v;
        graph[v].push_back(u);
        graph[u].push_back(v);
    }
    dfs(1,0);
    d[maxx]=0,dfs(maxx,0);
    cout<

OpenJ_Bailian-1125

  1. 多源汇最短路问题
  2. 采用Floyd算法
  3. 后横向比较第i个人出发传播给所有人的时间
  4. 最后比较哪个股票经纪人传播时间最少

AC代码

#include
using namespace std;
const int N=105;
int d[N][N],n;
void floyd(){
	for(int k=1;k<=n;k++){
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
			}
		}
	}
}
int main(){
	ios_base::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
	cin>>n;
	while(n!=0){
		for(int i=1;i<=n;i++){
			int m,v,w;
			cin>>m;
			for(int j=1;j<=m;j++){
				cin>>v>>w;
				d[i][v]=w;
			}
		}
		floyd();
		
		int maxx=1e9,num=0;
		for(int i=1;i<=n;i++){
			int res=0;
			for(int j=1;j<=n;j++){
				if(i==j) continue;
				res=max(res,d[i][j]);
			}
			if(maxx>res) maxx=res,num=i;
		}
		if(maxx==1e9) cout<<"disjoint"<>n;
	}
	return 0;
}

你可能感兴趣的:(算法,图论,数据结构,笔记,动态规划)