题意:给出有向无环图,删去若干条边,使得满足以下两个条件:
(1)对于每一个结点,其出边比原来少,除非初始出边就为0;
(2)对于每一个结点,其入边比原来少,除非初始入边就为0;
操作后,选择一个点集S,S中的任意两个元素u,v都满足:u能到v或者v能到u。
请得到最大的|S|
题解:由于是个有向无环图。所以S中的点一定是从u能到v,而v到不了u。
那么S集合一定是一条链上的所有点;
发现了这个问题后就好写了,可以发现用dp做。
状态表示:f[u]表示操作后以u为起点的最长链。
状态转移:f[u]=f[u]+max(f[v1],f[v2],f[v3]...);
状态转移的条件:u结点的出度>1,v结点的入度>1
为什么是这样转移,为什么是这样的条件,自己画个图想把,很容易想明白的。
最后答案就是所有点的f[u]中的最大值
#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
int n,m,idx;
int h[maxn];
struct Edge{
int v,nxt;
}edge[maxn<<1];
int f[maxn],din[maxn],dout[maxn];
void add(int u,int v){
edge[idx].v=v;
edge[idx].nxt=h[u];
h[u]=idx++;
}
void dfs(int u){
if(f[u]) return ;
f[u]=1;
for(int i=h[u];~i;i=edge[i].nxt){
int v=edge[i].v;
dfs(v);
}
int d=0;
for(int i=h[u];~i;i=edge[i].nxt){
int v=edge[i].v;
if(dout[u]>1&&din[v]>1)
d=max(d,f[v]);
}
f[u]+=d;
}
void solve(){
memset(h,-1,sizeof(h));
cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v;
cin>>u>>v;
add(u,v);
din[v]++;dout[u]++;
}
for(int i=1;i<=n;i++){
if(!din[i]){
dfs(i);
}
}
int ans=0;
for(int i=1;i<=n;i++){
ans=max(ans,f[i]);
}
cout<<ans<<'\n';
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t;t=1;
while(t--) solve();
return 0;
}