HDU 5452 Minimum Cut(LCA & RMQ & DFS)——2015 ACM/ICPC Asia Regional Shenyang Online

51 篇文章 0 订阅
17 篇文章 0 订阅

Minimum Cut

Time Limit: 3000/2000 MS (Java/Others)    Memory Limit: 65535/102400 K (Java/Others)


Problem Description
Given a simple unweighted graph  G  (an undirected graph containing no loops nor multiple edges) with  n  nodes and  m  edges. Let  T  be a spanning tree of  G .
We say that a cut in  G  respects  T  if it cuts just one edges of  T .

Since love needs good faith and hypocrisy return for only grief, you should find the minimum cut of graph  G  respecting the given spanning tree  T .
 

Input
The input contains several test cases.
The first line of the input is a single integer  t (1t5)        which is the number of test cases.
Then  t  test cases follow.

Each test case contains several lines.
The first line contains two integers  n (2n20000)       and  m (n1m200000) .
The following  n1  lines describe the spanning tree  T  and each of them contains two integers  u  and  v  corresponding to an edge.
Next  mn+1  lines describe the undirected graph  G  and each of them contains two integers  u  and  v  corresponding to an edge which is not in the spanning tree  T .
 

Output
For each test case, you should output the minimum cut of graph  G  respecting the given spanning tree  T .
 

Sample Input
  
  
1 4 5 1 2 2 3 3 4 1 3 1 4
 

Sample Output
  
  
Case #1: 2
 

Source
 
/*********************************************************************/

题意:给你一个无向图G(无自环和重边),问你至少要去掉几条边才能使G不连通,需要满足的条件是去掉的边需包含一条所给的图G的生成树的边

换句话说即删除最少的边使得G图变得不连通,删除的其中一条边属于生成树T

为了方便理解,拿样例做个说明


上图中红色部分为图G的生成树

如果一条边e(u,v)不属于生成树T,那么对于u和 v来说它连接到其他子树的贡献是1,
所以要num[u]++, num[v]++; (num存的是删除某个点需要删除的边的数量)!

如果e(u,v)这条边不是LCA(u,v)的这个点所要删除的边。那么两个端点的贡献就是2!所以要num[tt]-=2;

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define INF 0x3f3f3f3f
const int maxn = 80010;
const int maxm = 26;
int dp[2*maxn][maxm];  //数组开到2*N,因为遍历后序列长度为2*n-1
bool vis[maxn];
struct edge
{
    int from, to;
    int next;
} e[2*maxn];
int tot,head[maxn];
int cnt;
int num[maxn];
void init()
{
    memset(head,-1,sizeof(head));
    memset(vis,false,sizeof(vis));
    memset(num,0,sizeof(num));
    cnt = 0;
}
void addedge(int u, int v)
{
    e[cnt].from = u;
    e[cnt].to = v;
    e[cnt].next = head[u];
    head[u] = cnt++;
}
int ver[2*maxn], R[2*maxn], first[maxn];
//ver:节点编号 R:深度 first:点编号位置
void dfs(int u ,int dep)
{
    vis[u] = true;
    ver[++tot] = u;
    first[u] = tot;
    R[tot] = dep;
    for(int k=head[u]; k!=-1; k=e[k].next)
        if( !vis[e[k].to] )
        {
            int v = e[k].to;
            dfs(v, dep+1);
            ver[++tot] = u;
            R[tot] = dep;
        }
}
void ST(int n)
{
    for(int i=1; i<=n; i++)
        dp[i][0] = i;
    for(int j=1; (1<<j)<=n; j++)
    {
        for(int i=1; i+(1<<j)-1<=n; i++)
        {
            int a = dp[i][j-1] , b = dp[i+(1<<(j-1))][j-1];
            dp[i][j] = R[a]<R[b]?a:b;
        }
    }
}
//中间部分是交叉的。
int RMQ(int l,int r)
{
    int k=0;
    while((1<<(k+1))<=r-l+1)
        k++;
    int a = dp[l][k], b = dp[r-(1<<k)+1][k]; //保存的是编号
    return R[a]<R[b]?a:b;
}

int LCA(int u ,int v)
{
    int x = first[u] , y = first[v];
    if(x > y) swap(x,y);
    int res = RMQ(x,y);
    return ver[res];
}

int DFS(int u,int fa)
{
    for(int i = head[u]; ~i; i = e[i].next)
    {
        int v = e[i].to;
        if(v == fa)
            continue;
        DFS(v, u);
        num[u]+=num[v];
    }
    return 0;
}

int main()
{
    int t;
    int cas = 0;
    int n, m;
    scanf("%d",&t);
    while(t--)
    {
        init();
        scanf("%d%d",&n,&m);
        int u, v;
        for(int i = 0; i < n-1; i++)
        {
            scanf("%d%d",&u,&v);
            addedge(u, v);
            addedge(v, u);
        }
        for(int i = n; i <= m; i++)
        {
            scanf("%d%d",&u,&v);
            int tt = LCA(u, v);
            num[u]++;
            num[v]++;
            num[tt]-=2;//一条边两个端点 贡献为2
        }
        DFS(1, 1);
        int ans = INF;
        for(int i = 2; i <= n; i++)
        {
            ans = min(ans, num[i]+1);
        }
        printf("Case #%d: %d\n",++cas,ans);
    }
    return 0;
}
菜鸟成长记


不好意思,谢谢各位帮我纠正了此题错误的做法,是我考虑得不够全面,而且被水数据蒙骗了一把,万分感谢大家!此外,身为菜鸟的我实在是有心无力,目前还无法自己A掉此题,故转载了大神的解法,未经其同意擅自转载表示抱歉,@WDNLWZD,以下水过的解法会留着引以为戒,愿与大家共勉


题意:给你一个无向图G(无自环和重边),问你至少要去掉几条边才能使G不连通,需要满足的条件是去掉的边需包含一条所给的图G的生成树的边

换句话说即删除最少的边使得G图变得不连通,删除的其中一条边属于生成树T

为了方便理解,拿样例做个说明


上图中红色部分为图G的生成树

假设我们暂时先不考虑生成树,那么要使图G不连通,我们只需删去与度数最小的结点相连的边即可使图G不连通

而多考虑一个生成树的话,如果仍删去与度数最小的结点相连的边,有可能会删去生成树的两条边(如图中删去与结点2相连的边),因此,我们只需删去与[(仅与1条生成树边相连的且度数最小的)结点相连的]边即可

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<deque>
#include<functional>
#include<iterator>
#include<set>
#include<utility>
#include<stack>
#include<queue>
using namespace std;
#define maxn 22222
#define inf 1500000007
int deg1[maxn],deg2[maxn];
int main()
{
    int i,j,k,n,x,y,m,T,p=1;
    scanf("%d",&T);
    while(T--)
    {
        memset(deg1,0,sizeof(deg1));
        memset(deg2,0,sizeof(deg2));
        scanf("%d%d",&n,&m);
        for(i=1;i<=n-1;++i)
        {
            scanf("%d%d",&x,&y);
            deg1[x]++;
            deg1[y]++;
        }
        for(;i<=m;++i)
        {
            scanf("%d%d",&x,&y);
            deg2[x]++;
            deg2[y]++;
        }
        m=5555555;
        for(i=1;i<=n;++i)
        {
            if(deg1[i] == 1)
                m = min(m,deg2[i]);
        }
        printf("Case #%d: %d\n",p++,m+1);
    }
    return 0;
}
菜鸟成长记


评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值