这道题从看到到做出来有两周吧,一直头铁没有看题解,有一天走路的时候突然就想懂了。有点爽!
该题的子问题是给一个图,让找其中一个子图使得子图中的每个节点度都大于等于k。怎么解决?
把这个图里面小于k个度的结点都删掉不就行了,注意这里要用dfs,因为删除u结点后,可能导致v结点的度也不到k了(为了避免重复删除建了一个erase数组)。想到这个的话就差不多做出来了
肯定能想到反向回答问题,因为删边好解决。把那两个点的度都减一,如果小于k就删除掉。【这里删边我用了map,感觉很神器,因为直接按下标就能删了】
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<queue> #include<map> #include<algorithm> #include<vector> #define INF 2e9 using namespace std; map<int,bool> mp[200005]; vector<int> ans; int u[200005],v[200005],degree[200005],k; int erase[200005]; int delet(int u){//返回删除u这个人,所产生的总效应(导致一共要删多少人) int cnt=1;//已经删了u这个人 erase[u]=1; map<int,bool>::iterator it=mp[u].begin(); for(;it!=mp[u].end();it++){ int v=it->first; if( !erase[v] ){//只有当v没被删除的时候才需要考虑 degree[v]--; if( degree[v]<k ) cnt+=delet(v); } } return cnt; } int main(){ ios::sync_with_stdio(false); int n,m; cin>>n>>m>>k; for(int i=1;i<=m;i++){ cin>>u[i]>>v[i]; degree[ u[i] ]++; degree[ v[i] ]++; mp[ u[i] ][ v[i] ]=1; mp[ v[i] ][ u[i] ]=1; } int count=n; // cout<<count<<endl; for(int i=1;i<=n;i++){// if( !erase[i] && degree[i]<k ) { count-=delet(i); // cout<<i<<" "<<count<<endl; } } // cout<<"!!!"<<endl; for(int i=m;i>=1;i--){ // cout<<count<<endl; ans.push_back( count ); if( erase[ u[i] ] || erase[ v[i] ] ) continue; // cout<<u[i]<<" "<<v[i]<<" "<<degree[ u[i] ]<<" "<<degree[ v[i] ]<<endl; degree[ u[i] ]--; degree[ v[i] ]--; mp[ u[i] ].erase( v[i] ); mp[ v[i] ].erase( u[i] ); if( degree[ u[i] ]<k ) count-=delet( u[i] ); if( !erase[ v[i] ] && degree[ v[i] ]<k ) count-=delet( v[i] );//删除u[i]可能导致把v[i]删除掉 } reverse(ans.begin(),ans.end()); for(int i=0;i<ans.size();i++) cout<<ans[i]<<endl; return 0; }