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);

浙公网安备 33010602011771号