浅谈反图
rxr2018360074 · · 算法·理论
在做完这题后,我不禁陷入思考“反图”这个东西。以往,没有任何人给我详细讲过这种思想(虽然好像不需要讲),就好似幽灵一般凭空出现在了解题所需的方法当中,所以我们今天就来讲讲它。
何为反图
顾名思义,返图就是和题目中的输入反着来(有向图),如果题目说
实现
极为简单,一般只用改一行。
原本:
for(int i=1;i<=m;i++){
int a,b;
cin>>a>>b;
g[a].push_back(b);
}
现在:
for(int i=1;i<=m;i++){
int a,b;
cin>>a>>b;
g[b].push_back(a);
}
应用
反图有一个重要的性质:如果在原图中
例题一:给定一张
解法:
显然,每一次都去从当前点搜过不了,所以考虑在操作
但是,如果你考虑每个点能否到达黑点和暴力也没什么区别,所以我们从黑点的角度考虑。根据刚刚的结论,我们知道,只要反图上能从黑点走到某个点,那么在原图上就一定能从那个点走到黑点。
所以建立反图,加入黑点时用 BFS 搜索。
代码:
#include<bits/stdc++.h>
using namespace std;
vector<int> g[1000000];
bool can[1000000];
int main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=m;i++){
int a,b;
cin>>a>>b;
g[b].push_back(a);
}
int q;
cin>>q;
while(q--){
int op;
cin>>op;
if(op==1){
int v;
cin>>v;
if(can[v]){
continue;
}
can[v]=1;
queue<int> q;
q.push(v);
while(q.size()){
int t=q.front();
q.pop();
for(auto d:g[t]){
if(can[d]){
continue;
}
q.push(d);
can[d]=1;
}
}
}
else{
int v;
cin>>v;
cout<<(can[v]?"Yes":"No")<<endl;
}
}
return 0;
}
例题二:给定一张
思路:对于 DAG 上的 dp,我们一般采用拓扑序解决,但现在你同样无法一个个搜,所以考虑建立反图,因为一号在原图上不会有向其他点的边(有了直接删,反正没用),所以
讲一下一种可能的 Hack:如果某个点的出度为
代码:
#include<bits/stdc++.h>
using namespace std;
vector<int> g[1000000];
int in[1000000];
long long dp[1000000];
int main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=m;i++){
int a,b;
cin>>a>>b;
if(a==1){ //特判1:1出去的边一定没用
continue;
}
g[b].push_back(a);
in[a]++;
}
for(int i=2;i<=n;i++){
if(in[i]==0){
for(auto v:g[i]){
in[v]--;
}
}
} //特判2:入度为零的点直接删
queue<int> q;
dp[1]=1;
q.push(1);
while(q.size()){
int u=q.front();
q.pop();
for(auto v:g[u]){
dp[v]+=dp[u];
in[v]--;
if(in[v]==0){
q.push(v);
}
}
}
for(int i=1;i<=n;i++){
cout<<dp[i]<<" ";
}
return 0;
}
/*
Hack
7 10
1 5
5 6
2 1
3 1
2 3
4 2
4 3
2 7
3 7
4 7
1 2 1 3 0 0 0
*/