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