蓝桥杯 历届试题(只有40分,强联通求割点个数)

21 篇文章 0 订阅
5 篇文章 0 订阅

//在网上看了看其他人的题解,有拿并查集判断去除某个点后两个是否属于同一棵树的,有用DFS判断从某点到某点的方法有几个,顺便记录路上经过点,到达则每个点经过次数加一,最后判断方法和次数是否一致,一致则说明为必经dian ,....也可以用深搜和广搜判断去除某一点后,是否还能抵达.....

我看了看   好像属于割点,可以用Tarjan做......然后  在网上找了个关于强联通的模板(POJ1236).....求得是所有点组成的图的割点,,,,所以,解法还是和dfs或者bfs差不多,,都是去除某个点,然后判断是否属于同一个数组里......

但是  应该还是有问题,,只有40分....待解决.............

 

https://blog.csdn.net/qq_35078631/article/details/54705968的博文也用了Tarjan.....模板都是一样的,不过他新建了一个数组endishere,用来标记起点到终点的割点...标记方法在我看来是十分巧妙的...即不断往下搜索...直到终点,然后标记终点的的上个点,回溯即可将改路径上走过的点标记..之后判断1-n这n个点,被标记并且是割点即可....

模板应该如下..(能不能过..我没试过...等想写了改下提交试试)

 

//判断是否为割点
void FindCutNode(int dep, int u)
{
	dfn[u] = low[u] = dep;
	for(int i=head[u]; ~i; i=edge[i].next){
		int v = edge[i].v;
		//if(!dfn[v])//不可以用这个条件....会出错,还是再用个数组标记吧
            if(!vis[v])  {
		 vis[v] = true;
        	parent[v] = u; 

			FindCutNode(dep+1, v);
			if(u == rt){//此时访问的节点和 
				rt_num++;
if(v==end || endishere[v]==1){    //记录start和end中经过的点,如果子树是end或者子树已经为1则标1 
                endishere[u]=1;              //即搜索到终点end位置,会标记,然后回溯便可将从起点到终点所有的点标记,,后续做出来里即可,即判断cut[]和endishere[]
            }
			}
			     else if(parent[u] != v && dfn[v] < low[u])//v的父节点不能用来跟新他的low值 
                         {
				low[u] = min(low[u], low[v]);
				
				if(low[v] > dfn[u]){
					cut[u] = true;
				}
			} 
			
		}
		else low[u] = min(low[u], dfn[v]);
	}
}

 


//强联通求割边和割点的问题 
#include <string.h>
#include<iostream>
#include <stdio.h>
#define V    105
#define E    100500
const int inf = 0x3f3f3f;
using namespace std;

struct edge
{
    int to, next;
}Edge[E];

int head[V], e, n, m;

int indeg[V], outdeg[V]; //点的入度和出度数
int belong[V], low[V], dfn[V], scc, cnt;//dfn[]:遍历到u点的时间; low[]:u点可到达的各点中最小的dfn[v]
int S[V], top;
bool vis[V];//v是否在栈中
bool cut[V];
int addedge(int u, int v)
{
    Edge[e].to = v;
    Edge[e].next = head[u];
    head[u] = e++;
    return 0;
}

//int cont = 0; 
 
void tarjan(int u)
{
    int v;
    dfn[u] = low[u] = ++cnt;
    S[top++] = u;
    vis[u] = true;
    for (int i=head[u]; i!=-1; i=Edge[i].next)
    {
        v = Edge[i].to;
        if (dfn[v] == 0)//v点未遍历
        {
            tarjan(v);
         /*   
			if (low[v] > dfn[u])
            {
            	cont++;
				cut[u] = true; 
				cout << u << endl; //如果满足这个条件则u 为此时该图的割点
			}
            */
			low[u] = low[u] < low[v] ? low[u] : low[v];//回溯保证low为所联系的最小值
        }
        
        else if (vis[v] && low[u] > dfn[v])//v在栈中,修改low[u]
            low[u] = dfn[v];
    }

    if (dfn[u] == low[u])//u为该强连通分量中遍历所成树的根
    {
        ++scc;
        do
        {
            v = S[--top];//栈中所有到u的点都属于该强连通分量,退栈
            vis[v] = false;
            belong[v] = scc;
        } while (u != v);
    }

}

int solve(int i)
{
    scc = top = cnt = 0;
    memset(dfn, 0, sizeof(dfn));
    memset(vis, false, sizeof(vis));
    dfn[i] = low[i]= inf;
    for (int u=1; u<=n; ++u)
        if (dfn[u] == 0)
            tarjan(u);
    return scc;
}

void count_deg()
{
    memset(indeg, 0, sizeof(indeg));
    memset(outdeg, 0, sizeof(outdeg));
    for (int u=1; u<=n; ++u)
        for (int i=head[u]; i!=-1; i=Edge[i].next)
        {
            int v = Edge[i].to;
            if (belong[u] != belong[v])
            {
                indeg[belong[v]]++;
                outdeg[belong[u]]++;
            }
        }
}

int main()
{
    int u, v, i;
    while (~scanf("%d%d", &n, &m))
    {
    	memset(cut, false, sizeof(cut));
        e = 0;
        memset(head, -1, sizeof(head));
        for(int i=0; i<m; i++){
        	cin >> u >> v; 
            addedge(u, v);
            addedge(v, u);
        }
        int l, r;
        
        cin >> l >> r;
        
		int cutn = 0;
        //将i点去除后,判断l与r是否在同一个树里...若不在则为割点 
		for(int i=1; i<=n; i++){
        
			solve(i);
			
			
        if (scc == 1)
        	;
        	//cout << -1 << endl;
           // printf("1\n0\n");
        else
        {
        	if(belong[l] != belong[r])
        	 {
        	 	//因为自身去除后.....也属于一种情况... 
        	 	if(i == l || i == r)
        	 		continue;
       // 	 	cout << i << endl;
			 	cutn++;
			 }
         /*   count_deg();
            int inc = 0, outc = 0;
            for (int i=1; i<=scc; ++i)
            {
                if (indeg[i] == 0)
                    inc++;
                if (outdeg[i] == 0)
                    outc++;
            }
            printf("%d\n%d\n", inc, (inc > outc ? inc : outc));*/
        }
        
    }
    if(cutn == 0)
    	cout << -1 << endl; 
    else
		cout << cutn <<endl;
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值