HDU 4635强连通分量

该博客讨论了一种图论问题,即如何在保持图简单且不形成强连通图的前提下,计算可以添加的最大边数。作者通过一加边策略,直至图仅分为两个单向连接的点集,然后利用均值不等式优化策略,寻找最小的强连通分量以最大化可添加的边数。在实现过程中,使用了Tarjan算法进行强连通分量检测,并通过优化找出最佳解。博客中提到了对细节的忽视导致的错误,并分享了从错误中学习的经验。
摘要由CSDN通过智能技术生成
题目链接

http://acm.hdu.edu.cn/showproblem.php?pid=4635

题意

给出一张无向简单图,问保持其简单图性质且不生成强连通图的前提下,可以加多少边。若已经强连通,答案为-1.

思路

易想到一加边策略,不断加边直到最终只划分为两个点集,两点集内强连通,两点集间只有单向边。设其较小的点集大小为x,易知此时边数为n*(n-1)-(n-x)*x。将这个数减去本来边的数量即为答案。

显然(均值不等式)我们应令x最小化。我们求SCC并缩点,新图中所有入度为0或者出度为0的点可以作为大小为x的点集。挑选其最小的更新答案即可。

教训/收获

没注意到入度为0或出度为0才能满足条件。还是太菜,继续修炼

代码
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib> 
#include<chrono>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
#define int long long
//#define double long double
using namespace std;
	typedef long long ll;
	const int maxn=200505;
	const int inf=0x3f3f3f3f3f3f3f3f;
	int n,m,k;
    int dfn[maxn],low[maxn];
    bool in_sta[maxn];
    stack<int>sta;
    int S_id[maxn];
    int scc_size[maxn];
    int scc_cnt;
    int head[maxn],cnt;
    int dfn_cnt;
    int in[maxn],out[maxn];
    struct Edge{
        int from,to,val,next;
    }edge[maxn];
    void init(){
        memset(head,-1,sizeof head);
        memset(dfn,0,sizeof dfn);
        memset(low,0,sizeof low);
        memset(in_sta,0,sizeof in_sta);
        memset(scc_size,0,sizeof scc_size);
        memset(in,0,sizeof in);
        memset(out,0,sizeof out);
        cnt=0;scc_cnt=0;dfn_cnt=0;
        while(sta.size())   sta.pop();
    }
    void add(int u,int v,int w){
        edge[cnt]={u,v,w,head[u]};
        head[u]=cnt++;
    }
    void tarjan(int x){
        dfn[x]=low[x]=++dfn_cnt;
        sta.push(x);in_sta[x]=1;
        for(int i=head[x];~i;i=edge[i].next){
            int y=edge[i].to;
            if(!dfn[y]){
                tarjan(y);
                low[x]=min(low[x],low[y]);
            }
            else if(in_sta[y]){
                low[x]=min(low[x],dfn[y]);
            }
        }
        if(dfn[x]==low[x]){
            scc_cnt++;
            int z;
            do{
                z=sta.top();sta.pop();
                in_sta[z]=0;
                S_id[z]=scc_cnt;
                scc_size[scc_cnt]++;
            }while(z!=x);
        }
    }
	signed main(){
        IOS
		#ifndef ONLINE_JUDGE
		    freopen("IO\\in.txt","r",stdin);
		    freopen("IO\\out.txt","w",stdout);
        #endif
        init();
		int tn=1;
		cin>>tn;
        for(int ttn=1;ttn<=tn;ttn++){
            cin>>n>>m;
            init();
            cout<<"Case "<<ttn<<": ";
            k=m;
            while(k--){
                int u,v;
                cin>>u>>v;
                add(u,v,0);
            }
            for(int i=1;i<=n;i++)
                if(!dfn[i]) tarjan(i);
            if(scc_cnt==1){
                cout<<-1<<endl;
                continue;
            }
            for(int i=0;i<cnt;i++){
                int uid=S_id[edge[i].from];
                int vid=S_id[edge[i].to];
                if(uid==vid)    continue;
                in[vid]++;
                out[uid]++;
            }
            int mi=inf;
            for(int i=1;i<=scc_cnt;i++){
                if(!in[i]||!out[i]){
                    mi=min(mi,(n-scc_size[i])*scc_size[i]);
                }
            }
            int ans=n*(n-1)-mi-m;
            
            cout<<ans<<endl;
        }
	} 
						
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值