Part 8.1 图的存储与遍历
这里的图论内容都比较简单,涉及图的存储以及遍历图的方式。
好骗!
还好不是刚学图论,不然要究极自闭了
1.P2661 信息传递
看似像个憨批题
其实T到哭
求最小强连通分量
#include<bits/stdc++.h>
using namespace std;
const int maxn=4e5+5,maxm=4e5+5;
int n,m;
int dfn[maxn],low[maxn],vis[maxn],z[maxn],color[maxn],cnt[maxn],du[maxn],t,k,tot;
vector<int>e[maxm];
int minn=0x3f3f3f3f;
void tarjan(int u){
dfn[u]=low[u]=++tot;
z[++k]=u;//入栈
vis[u]=1;
for(int i=0;i<e[u].size();i++){
if(!dfn[e[u][i]]){
tarjan(e[u][i]);
low[u]=min(low[u],low[e[u][i]]);
}
else if(vis[e[u][i]]){
low[u]=min(low[u],dfn[e[u][i]]);
}
}
if(low[u]==dfn[u]){
t++;//连通分量的标号
do{
color[z[k]]=t; //属于这个连通分量
cnt[t]++; //记录这个环中有多少个点
vis[z[k]]=0;
k--; //出栈
}while(u!=z[k+1]);
}
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
int v;
scanf("%d",&v);
e[i].push_back(v);
}
for(int i=1;i<=n;i++){
if(!dfn[i])tarjan(i);
}
for(int i=1;i<=t;i++){
// cout<<cnt[i]<<" ";
if(cnt[i]>1)minn=min(minn,cnt[i]);
}
cout<<minn;
return 0;
}
2.P2921 [USACO08DEC]Trick or Treat on the Farm
跟上一题差不多,呵呵
先缩点然后记忆化搜索,走到强连通分量就必定可以走到已经走过的点,或者走到自己也可以
如果不记忆化就T爆
核心代码:
int dfs(int u){
if(ans[u])return ans[u];
if(cnt[color[u]]>1||e[u][0]==u)return cnt[color[u]];
ans[u]=dfs(e[u][0])+1;
return ans[u];
}
完整代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5,maxm=1e5+5;
int n,m,vis[maxn],ans[maxm],color[maxn],cnt[maxn];
int dfn[maxn],low[maxn],z[maxn],t,k,tot;
vector<int>e[maxn];
void tarjan(int u){
dfn[u]=low[u]=++tot;
z[++k]=u;
vis[u]=1;
for(int i=0;i<e[u].size();i++){
int v=e[u][i];
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(vis[v]){
low[u]=min(low[u],dfn[v]);
}
}
if(low[u]==dfn[u]){
t++;
do{
color[z[k]]=t;
cnt[t]++;
vis[z[k]]=0;
k--;
}while(u!=z[k+1]);
}
}
int dfs(int u){
if(ans[u])return ans[u];
if(cnt[color[u]]>1||e[u][0]==u)return cnt[color[u]];
ans[u]=dfs(e[u][0])+1;
return ans[u];
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
int u,v;
scanf("%d",&v);
e[i].push_back(v);
}
for(int i=1;i<=n;i++){
if(!dfn[i])tarjan(i);
}
for(int i=1;i<=n;i++){
printf("%d\n",dfs(i));
}
return 0;
}