图论
技巧:要判断一遍dfs能否走完所有节点,可以判断这些节点的父亲是否相同,如果相同则可以走完所有节点,否则不能。
链式前向星存图
struct edge{
int val,v,next;
}edge[2005];
int head[2005],cnt;
void init()
{
for(int i = 0; i <= n; i++) head[i] = -1;
}
void add(int u,int v,int val)
{
edge[cnt].v = v;
edge[cnt].val = val;
edge[cnt].next = head[u];
head[u] = cnt++;
}
int main()
{
init();
return 0;
} 打印路径
void path(int x,int s,int d)
{
if(s == d) {
printf("%d",s);
return;
}
path(s,pre[d]);//先输出前一个
printf(" %d",d);//再输出后一个
} SPFA+链式前向星模板
O(n*m)支持负权
SPFA 队列+链式前向星模板
struct S{
int next,e,v;
}edge[105];
int head[105];
int d[105];
bool inq[105];
int n,m,a,b,c,cnt;
void add(int a,int b,int c)
{
edge[++cnt].next = head[a];
edge[cnt].e = b;
edge[cnt].v = c;
head[a] = cnt;
}
void spfa(int t)
{
for(int i = 0; i <= n; i++){
d[i] = 1000000;
inq[i] = false;
}
queue<int>q;
q.push(1);
d[1] = 0;
inq[1] = true;
while(!q.empty()){
int s = q.front();
q.pop();
inq[s] = false;
for(int i = head[s];i != 0; i = edge[i].next){
int u = edge[i].e,w = edge[i].v;
if(d[u] > d[s] + w){
d[u] = d[s] + w;
if(!inq[u]){
inq[u] = true;
q.push(u);
}
}
}
}
printf("%d\n",d[n]);
}
int main()
{
while(~scanf("%d%d",&n,&m)){
if(n == 0 && m == 0) break;
cnt = 0;
for(int i = 1; i <= n; i++) edge[i].next = 0,head[i] = 0;
for(int i = 1; i <= m; i++){
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
add(b,a,c);
}
spfa(1);
}
return 0;
} 最小生成树
kruskal算法:对每条变按边权值从小到大排列,然后优先选取边权值小的边,判断两点的父亲是否相同,如果相同不选,不相同加入,利用并查集判断。
https://nanti.jisuanke.com/t/43111
这道题只要选出n-k个人即可,所有人直接或间接认识说明所有人可以用一根线连起来(也可以说父亲相同)。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <stack>
#include <queue>
#include <string.h>
#include <cmath>
#include <bitset>
#include <set>
#include <map>
#define ll long long
const int inf = 0x3f3f3f3f;
const int mod = 1e9+7;
const double p = 3.141592653589793238462643383;
using namespace std;
struct edge{
int u,v,val;
bool operator < (const edge &a) const{
return val < a.val;
}
}edge[400005];
int f[200005];
int find(int x)
{
if(x != f[x]) f[x] = find(f[x]);
return f[x];
}
int main()
{
ios::sync_with_stdio(false);
int m,n,k;
cin >> n >> m >> k;
for(int i = 1; i <= n; i++) f[i] = i;
for(int i = 1; i <= m; i++) cin >> edge[i].u >> edge[i].v >> edge[i].val;
sort(edge+1,edge+m+1);
int cnt = 1;
ll sum = 0;
for(int i = 1; i <= m; i++){
int u = edge[i].u;
int v = edge[i].v;
u = find(edge[i].u);
v = find(edge[i].v);
if(u == v) continue;
f[u] = v;
sum += 1ll*edge[i].val;
if(cnt++ == n - k) break;
}
cout << sum << endl;
return 0;
} K短路
相关问题
1:判断一条边是否在1-n的最短路上
先求源点为1的单源最短路dist1,再求以n为源点的单源最短路dist2,然后再枚举每条边(u,v),如果dist1[u]+len[u,v]+dist2[v]=min或者是dist1[v]+len[u,v]+dist2[u]=min,则说明这条边在最短路上,否则不在。
https://ac.nowcoder.com/acm/contest/5158/E
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <stack>
#include <queue>
#include <string.h>
#include <cmath>
#include <bitset>
#include <set>
#include <map>
#define ll long long
const int inf = 0x3f3f3f3f;
const int mod = 1e9+7;
const double p = 3.141592653589793238462643383;
using namespace std;
struct Edge{
int u,v,w;
Edge(int f,int to,int val){
u = f,v = to,w = val;
}
};
struct node{
int opt;
ll va;
node(int a,ll b){
opt = a;
va = b;
}
bool operator < (const node &a) const{
return va > a.va;
}
//或者下面这样写
//friend bool operator < (node a,node b){
// return a.va > b.va;
//}
};
ll vis[1000005],dvis[100005],h[1000005],cnt,n,m,k,t;
ll l[1000005],l1[2000005],l2[1000005];
int U[1000005],V[1000005],W[1000005];
vector<Edge>edge[1000005];
void dijkstra(int d)
{
for(int i = 1; i <= n; i++) l[i] = mod*(double)100000,vis[i] = 0;
priority_queue<node>q;
l[d] = 0;
q.push(node(d,0));
while(!q.empty()){
node s = q.top();//node s只能写在这。
int u = s.opt;
ll W = s.va;
//cout << u << " ";
q.pop();
if(vis[u]) continue;
vis[u] = 1;
for(int i = 0; i < edge[u].size(); i++){
Edge y = edge[u][i];
//cout << i << endl;
if(vis[y.v]) continue;
if(l[y.v] > W + y.w){
l[y.v] = W + y.w;
q.push(node(y.v,l[y.v]));
}
}
}
}
int find(int x)
{
if(x == h[x]) return x;
return h[x] = find(h[x]);
}
void merge(int x,int y)
{
x = find(x);
y = find(y);
if(x != y) h[y] = x;
}
int main()
{
ios::sync_with_stdio(false);
cin >> n >> m;
int a,b,c;
for(int i = 0; i <= n; i++) h[i] = i;
for(int i = 1; i <= m; i++){
cin >> U[i] >> V[i] >> W[i];
edge[U[i]].push_back(Edge(U[i],V[i],W[i]));
edge[V[i]].push_back(Edge(V[i],U[i],W[i]));
}
dijkstra(1);
for(int i = 1; i <= n; i++) l1[i] = l[i];
dijkstra(n);
for(int i = 1; i <= n; i++) l2[i] = l[i];
for(int i = 1; i <= m; i++){
if(l1[U[i]] + 1ll*W[i] + l2[V[i]] == l1[n] || l1[V[i]] + 1ll*W[i] + l2[U[i]] == l1[n]){
continue;
}
merge(U[i],V[i]);
}
int cnt = 0;
for(int i = 1; i <= n; i++){
if(h[i] == i) cnt++;
}
if(cnt == 1) cout << "YES\n";
else cout << "NO\n";
return 0;
} 2:n个起点,m个终点,求这n个起点中的任意一点到m个终点中任意一点的最短路
建一个超级源点s,从s的n个起点分别连一条权值为零的边,建立一个超级汇点,从m个终点到t分别连一条权值为零的边,然后求s-t的最短路即可。
3:求有向图中所有点到n点的最短路。
反向建图,以n为源点求单源最短路。
(起点和终点交换,方向相反,问题等效)
https://www.jisuanke.com/course
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <stack>
#include <queue>
#include <string.h>
#include <cmath>
#include <bitset>
#include <set>
#include <map>
#define ll long long
const int inf = 0x3f3f3f3f;
const int mod = 1e9+7;
const ll ds = 1e15+7;
const double p = 3.141592653589793238462643383;
using namespace std;
struct edge{
int v,w,next;
}edge[2][60005];
struct node{
int opt;
ll w;
node(int a,ll k){
opt = a;
w = k;
}
bool operator < (const node &p) const{
return w > p.w;
}
};
int head[2][20005],v[20005],cnt,cnt1,m,n,t;
void add(int u,int v,int w)
{
edge[0][cnt].next = head[0][u];
edge[0][cnt].v = v;
edge[0][cnt].w = w;
head[0][u] = cnt++;
}
void add1(int u,int v,int w)
{
edge[1][cnt1].next = head[1][u];
edge[1][cnt1].v = v;
edge[1][cnt1].w = w;
head[1][u] = cnt1++;
}
void init()
{
for(int i = 1; i <= n; i++) head[0][i] = -1,head[1][i] = -1;
}
void dijkstra(int l,int dy,ll d[])
{
priority_queue<node>q;
for(int i = 1; i <= n; i++) d[i] = ds,v[i] = 0;
d[dy] = 0;
q.push(node(dy,0));
while(!q.empty()){
node s = q.top();
q.pop();
int t = s.opt;
ll w = s.w;
if(v[t]) continue;
v[t] = 1;
for(int i = head[l][t]; ~i; i = edge[l][i].next){
int va = edge[l][i].w;
int k = edge[l][i].v;
if(v[k]) continue;
if(d[k] > w + 1ll*va){
d[k] = w + 1ll*va;
q.push(node(k,d[k]));
}
}
}
}
int main()
{
ios::sync_with_stdio(false);
ll d[20005],d1[20005];
cin >> t;
while(t--){
cin >> n >> m;
int u,v,w;
cnt = 0,cnt1 = 0;
memset(d,0,sizeof(d));
memset(d1,0,sizeof(d1));
init();
for(int i = 1; i <= m; i++){
cin >> u >> v >> w;
add(u,v,w);
add1(v,u,w);
}
dijkstra(0,1,d);
dijkstra(1,1,d1);
// for(int i = 1; i <= n; i++) cout << d[i] << endl;
// for(int i = 1; i <= n; i++) cout << d1[i] << endl;
ll sum = 0;
for(int i = 1; i <= n; i++){
sum += d[i] + d1[i];
}
cout << sum << endl;
}
return 0;
} 树的重心
树的直径
1、直径两端点一定是两个叶子节点
2、距离任意点最远的点一定是直径的一个端点,这个基于贪心求直径方法的正确性可以得出
3、对于两棵树,如果第一棵树直径两端点为(u,v)(u,v),第二棵树直径两端点为(x,y)(x,y),用一条边将两棵树连接,那么新树的直径一定是u,v,x,y,u,v,x,y,中的两个点
证明:如果新树直径不是原来两棵树中一棵的直径,那么新直径一定经过两棵树的连接边,新直径在原来每棵树中的部分一定是距离连接点最远的点,即一定是原树直径的一个端点。
4、对于一棵树,如果在一个点的上接一个叶子节点,那么最多会改变直径的一个端点
证明:假设在xx下面接一个点yy,直径变成了(u,x)(u,x),原树直径为(a,b)(a,b),那么dis(u,x)>dis(a,b),dis(u,x)=dis(u,y)+1dis(u,x)>dis(a,b),dis(u,x)=dis(u,y)+1,即dis(u,y)+1>dis(a,b)dis(u,y)+1>dis(a,b),如果dis(u,y)<dis(a,b)dis(u,y)<dis(a,b),那么显然不成立;如果dis(u,y)=dis(a,b)dis(u,y)=dis(a,b),那么(u,y)(u,y)也是原树的直径,符合上述结论。
5、若一棵树存在多条直径,那么这些直径交于一点且交点是这些直径的中点
vector<int>a[100005];
int dp[100005],ans = 0;
void dfs(int so,int fa)
{
int l = a[so].size();
for(int i = 0; i < l; i++){
int v = a[so][i];
if(v == fa) continue;
dfs(v,so);
ans = max(ans,dp[so]+dp[v]+1);
dp[so] = max(dp[so],dp[v]+1);
}
} pta-紧急救援:djikstra
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <time.h>
#include <stack>
#include <queue>
#include <string.h>
#include <cmath>
#include <bitset>
#include <set>
#include <map>
#define ll long long
const int inf = 0x3f3f3f3f;
const int mod = 1e9+7;
const ll ds = 1e15+7;
const double p = 3.141592653589793238462643383;
using namespace std;
struct edge{
int v,next,w;
}edge[5005];
struct node{
int x,w;
node(int a,int b){
x = a;
w = b;
}
bool operator<(const node& a)const{
return w > a.w;
}
};
int n,m,s,d;
int val[505];
int head[505],pre[505],num[505],dir[505],vis[505],num1[505],cnt = 0;
void add(int u,int v,int w)
{
edge[cnt].v = v;
edge[cnt].w = w;
edge[cnt].next = head[u];
head[u] = cnt++;
}
void init()
{
for(int i = 0; i <= n; i++) head[i] = -1;
}
void dij(int s)
{
for(int i = 0; i <= n; i++) dir[i] = mod,vis[i] = 0;
priority_queue<node>q;
dir[s] = 0;
q.push(node(s,0));
num1[s] = val[s];
num[s] = 1;
while(!q.empty()){
node t = q.top();
q.pop();
int k = t.x;
if(vis[k]) continue;
vis[k] = 1;
for(int i = head[k]; ~i; i = edge[i].next){
//cout << i << endl;
int v = edge[i].v;
int w = edge[i].w;
if(vis[v]) continue;
if(dir[v] > t.w + w){
num[v] = num[k];
dir[v] = t.w + w;
num1[v] = num1[k] + val[v];
pre[v] = k;
q.push(node(v,dir[v]));
}
else if(dir[v] == t.w + w){
num[v] += num[k];
if(num1[v] < num1[k] + val[v])
num1[v] = num1[k] + val[v],pre[v] = k;
}
}
}
}
void print(int s,int d)
{
if(s == d){
printf("%d",s);
return;
}
print(s,pre[d]);
printf(" %d",d);
}
int main()
{
scanf("%d%d%d%d",&n,&m,&s,&d);
for(int i = 0; i < n; i++){
scanf("%d",&val[i]);
}
init();
int v,u,w;
for(int i = 1; i <= m; i++){
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
add(v,u,w);
}
dij(s);
printf("%d %d\n",num[d],num1[d]);
print(s,d);
return 0;
}
欧涛的最短路
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <stack>
#include <queue>
#include <string.h>
#include <cmath>
#include <bitset>
#include <set>
#include <map>
#include <list>
#define ll long long
const int inf = 0x3f3f3f3f;
const int mod = 1e9;
const ll ds = 1e15+7;
const double p = 3.141592653589793238462643383;
using namespace std;
struct edge{
int v,next;
double w;
}edge[5005];
struct node{
int x;
double len;
node(int a,double w){
x = a;
len = w;
}
bool operator < (const node& a) const{
return len > a.len;
}
};
struct D{
double x,y,z;
}a[605];
int cnt = 0,head[605],vis[605],pre[605],b[605],n;
double m,d[605];
void add(int u,int v,double w)
{
edge[cnt].v = v;
edge[cnt].w = w;
edge[cnt].next = head[u];
head[u] = cnt++;
}
void init()
{
for(int i = 0; i <= n+2; i++) head[i] = -1;
}
void dijkstra()
{
priority_queue<node>q;
for(int i = 1; i <= n+2; i++) d[i] = 1000000000,vis[i] = 0;
d[1] = 0;
q.push(node(1,0));
while(!q.empty()){
// cout << 1 << endl;
node s = q.top();
q.pop();
if(vis[s.x]) continue;
vis[s.x] = 1;
for(int i = head[s.x]; ~i; i = edge[i].next){
int v = edge[i].v;
double w = edge[i].w;
if(vis[v]) continue;
if(d[v] > d[s.x] + w){
d[v] = d[s.x] + w;
pre[v] = s.x;
q.push(node(v,d[v]));
}
}
}
}
int cnt1 = 0;
void get_path(int s,int t)
{
if(s == t){
b[cnt1++] = s;
return ;
}
get_path(s,pre[t]);
b[cnt1++] = t;
}
int main()
{
int x1,x2,y1,y2,z1,z2;
cin >> n >> m;
cin >> a[1].x >> a[1].y >> a[1].z >> a[n+2].x >> a[n+2].y >> a[n+2].z;
init();
for(int i = 2; i <= n+1; i++){
cin >> a[i].x >> a[i].y >> a[i].z;
}
double x;
for(int i = 1; i <= n+2; i++){
for(int j = i+1; j <= n+2; j++){
x = sqrt((a[i].x-a[j].x)*(a[i].x-a[j].x)+(a[i].y-a[j].y)*(a[i].y-a[j].y)+(a[i].z-a[j].z)*(a[i].z-a[j].z));
if(x <= m){
add(i,j,x);
add(j,i,x);
}
}
}
dijkstra();
if(d[n+2] == 1000000000){
cout << "-1" << endl;
}
else{
get_path(1,n+2);
printf("%.3f\n",d[n+2]);
cout << "Start ";
for(int i = 1; i < cnt1-1; i++){
cout << b[i] - 1 << " ";
}
cout << "End" << endl;
}
return 0;
}

查看9道真题和解析