057.拓扑排序
拓扑排序
-
有向无环图
-
可以判断环
流程
-
找到所有入度为0的点
-
将入度为0的点删除,它所指向的点的入度减1
-
重复,直到所有点都被删除
-
如果无法删除所有点,说明存在环
const int N=1e5;
vector<int>gra[N];
int in[N];
void top(int n){
for(int u=1;u<=n;++u){
for(int v:gra[u]){
in[v]++;
}
}
queue<int>q;
for(int i=1;i<=n;++i){
if(in[i]==0){
q.push(i);
}
}
while(q.size()){
int u=q.front();
q.pop();
for(int v:gra[u]){
if(--in[v]==0){
q.push(v);
}
}
}
}
以上只是大致流程
通常在拓扑过程中递推信息解题
习题
01 模板
const int N=105;
vector<int>gra[N];
int in[N];
void solve(){
int n;
cin>>n;
for(int u=1;u<=n;++u){
int v;
while(cin>>v&&v!=0){
gra[u].push_back(v);
in[v]++;
}
}
queue<int>q;
for(int i=1;i<=n;++i){
if(in[i]==0){
q.push(i);
}
}
while(q.size()){
int u=q.front();
q.pop();
cout<<u<<' ';
for(int v:gra[u]){
if(--in[v]==0){
q.push(v);
}
}
}
}
02 最大食物链总数
-
拓扑过程中传递信息
-
记录出度 -> 出度为0的为食物链顶端
const int N=5005;
const int P=80112002;
vector<int>gra[N];
int in[N],out[N];
int cnt[N];
void solve(){
int n,m,u,v;
cin>>n>>m;
while(m--){
cin>>u>>v;
out[u]++;
in[v]++;
gra[u].push_back(v);
}
queue<int>q;
for(int i=1;i<=n;++i){
if(in[i]==0){
cnt[i]=1;
q.push(i);
}
}
while(q.size()){
int u=q.front();
q.pop();
for(int v:gra[u]){
cnt[v]=(cnt[v]+cnt[u])%P;
in[v]--;
if(in[v]==0)q.push(v);
}
}
int ans=0;
for(int i=1;i<=n;++i){
if(out[i]==0){
ans=(ans+cnt[i])%P;
}
}
cout<<ans;
}
03 喧嚣于富有
- 拓扑过程中更新答案
class Solution {
public:
vector<int> loudAndRich(vector<vector<int>>& richer, vector<int>& quiet) {
int n=quiet.size();
vector<vector<int>>gra(n);
vector<int>in(n);
for(auto e:richer){
gra[e[0]].push_back(e[1]);
in[e[1]]++;
}
vector<int>ans(n);
for(int i=0;i<n;++i){
ans[i]=i;
}
queue<int>q;
for(int i=0;i<n;++i){
if(in[i]==0){
q.push(i);
}
}
while(q.size()){
int u=q.front();
q.pop();
for(int v:gra[u]){
if(--in[v]==0)q.push(v);
if(quiet[ans[v]]>quiet[ans[u]]){
ans[v]=ans[u];
}
}
}
return ans;
}
};
04 并行问题
- 完成当前任务的最早时间 = 完成前置任务的最晚时间 + 单独完成当前任务所需的时间
class Solution {
public:
int minimumTime(int n, vector<vector<int>>& relations, vector<int>& time) {
vector<vector<int>>gra(n);
vector<int>in(n);
for(auto e:relations){
int u=e[0]-1;
int v=e[1]-1;
gra[u].push_back(v);
in[v]++;
}
queue<int>q;
for(int i=0;i<n;++i){
if(in[i]==0)q.push(i);
}
vector<int>cnt(n);
for(int i=0;i<n;++i){
cnt[i]=time[i];
}
while(q.size()){
int u=q.front();
q.pop();
for(int v:gra[u]){
if(--in[v]==0)q.push(v);
cnt[v]=max(cnt[v],time[v]+cnt[u]);
}
}
int ans=0;
for(int x:cnt)ans=max(ans,x);
return ans;
}
};
05 火星词典
- 建图:字典序小的字母 指向 字典序大的字母
class Solution {
public:
string alienOrder(vector<string>& words) {
vector<vector<int>>gra(26);
vector<int>in(26,0);
unordered_set<int>vis;
for(string word:words){
for(char ch:word){
vis.insert(ch-'a');
}
}
int n=words.size();
for(int i=0;i+1<n;++i){
int ls=words[i].size();
int lt=words[i+1].size();
int j=0;
while(j<ls&&j<lt&&words[i][j]==words[i+1][j]){
j++;
}
if(j<ls&&j==lt)return "";
if(j<ls&&j<lt){
int u=words[i][j]-'a';
int v=words[i+1][j]-'a';
gra[u].push_back(v);
in[v]++;
}
}
string ans;
queue<int>q;
for(int i=0;i<26;++i){
if(vis.count(i)&&in[i]==0){
q.push(i);
}
}
while(q.size()){
int u=q.front();
q.pop();
ans.push_back('a'+u);
for(int v:gra[u]){
if(--in[v]==0)q.push(v);
}
}
return ans.size()==vis.size()?ans:"";
}
};
06 印戳问题
-
完全匹配的位置一定是最后盖章的
-
倒着考虑,本次的操作可以修改上次的错误
-
in[i]表示以 i 为起点,错误的数量
class Solution {
public:
vector<int> movesToStamp(string s, string t) {
int ls=s.size();
int lt=t.size();
vector<vector<int>>gra(lt);
vector<int>in(lt-ls+1,ls);
queue<int>q;
for(int i=0;i+ls<=lt;++i){
for(int j=0;j<ls;++j){
if(t[i+j]==s[j]){
if(--in[i]==0){
q.push(i);
}
}
else{
gra[i+j].push_back(i);
}
}
}
vector<bool>vis(lt,0);
vector<int>ans;
while(q.size()){
int u=q.front();
q.pop();
ans.push_back(u);
for(int i=0;i<ls;++i){
if(vis[u+i]==0){
vis[u+i]=1;
for(int v:gra[u+i]){
if(--in[v]==0){
q.push(v);
}
}
}
}
}
if(ans.size()!=lt-ls+1)return vector<int>();
reverse(ans.begin(),ans.end());
return ans;
}
};
07 圆桌会议
-
考虑图中的环
-
小环(节点数量 == 2)
-
大环(节点数量 >= 3)
-
对于一个大环,发现只可能坐下环上的所有元素,并且它们将整个圆桌都围起来了
-
对于一个小环(u,v),它们各自都可以再挂一条链,并且它们整体在圆桌上只占据一个线段
-
所以答案有两种可能
-
所有小环( size + deep[u] + deep[v])累加,其中小环的 size 为 2 ,deep[u]表示u身上最多可以挂多少节点
-
一个大环 ( size )
- 用拓扑处理环,跑一遍拓扑,只有环上的元素入度大于0
class Solution {
public:
int maximumInvitations(vector<int>& favorite) {
int n=favorite.size();
vector<int>in(n);
for(int x:favorite){
in[x]++;
}
queue<int>q;
for(int i=0;i<n;++i){
if(in[i]==0)q.push(i);
}
vector<int>cnt(n);
while(q.size()){
int u=q.front();
q.pop();
int v=favorite[u];
if(--in[v]==0)q.push(v);
cnt[v]=max(cnt[v],cnt[u]+1);
}
int ans1=0;
int ans2=0;
for(int u=0;u<n;++u){
if(in[u]){
in[u]=0;
int siz=1;
for(int v=favorite[u];v!=u;v=favorite[v]){
in[v]=0;
siz++;
}
if(siz==2){
ans1+=2+cnt[u]+cnt[favorite[u]];
}
else{
ans2=max(ans2,siz);
}
}
}
return max(ans1,ans2);
}
};
I am the bone of my sword

浙公网安备 33010602011771号