暑期个人赛--第六场--D(包含手写邻接链表模板~!!)

时间限制 1000 ms  内存限制 65536 KB

题目描述

在星际时代,每个帝国都靠着贸易路线连接着各个联盟星球,这些贸易路线都是双向可达的。一个帝国的综合实力由他贸易连接着的联盟星球数决定。
学姐作为Mays帝国的领袖,长期与Luke帝国保持着敌对关系,爱好和平的学姐希望结束长达几个世纪的战争,于是找实验室定做了一颗代号小苹果的炸弹,可以定点摧毁一颗星球,这颗星球被毁后,与它相连的全部贸易就都被切断了,这样Luke帝国可能就被切断为一个小联盟,他们就再也不会对学姐的地位构成威胁啦~
经过调查,Luke帝国为了节约经费,他的联盟星之间都有且仅有一条直接或间接的贸易通路。
现在给出Luke帝国的贸易线路,学姐想知道摧毁哪一颗行星可以使得分裂后的若干Luke联盟威胁最大的分部最小。

输入格式

输入有多组数据,组数不大于10组。每一组开头一行为n,m,表示Luke帝国的联盟星球数量,和贸易关系数,接下来m行,每行两个整数u,v,表示星球u,v之间存在直接的贸易路线,1<=u,v<=n,1<=n,m<=100000

输出格式

输出一个数表示推荐学姐摧毁的星球,如果有多解,输出编号最小的一个。

输入样例

5 4
1 2
1 3
1 4
4 5

输出样例

1


赛中提交:NULL

赛后AC:YES  (T T T WA WA WA AC)


题目大意:

给n个点和m个边,并且“联盟星之间都有且仅有一条直接或间接的贸易通路”

也就是说“点与点之间都有且仅有一条直接或间接的路径

也就是说不会成环,是一个无向无环图,可以看做是一棵树

然后要求拿走哪一个点之后,剩下的子树的最大值最小


思路:

树状DP

任选一个点只来一遍DFS,

DFS函数返回值是“不包括他的当前前驱节点在内的所有子孙(注意是子孙所有后代)的个数”

在DFS过程中通过先求出排除他的当前前驱节点后的最大子树mostw[i],然后对比这个最大子树和n-mostw[i]

得出的值就是真正的最大子树,赋给mostw[i]

最后再在main中取mostw[i]的最小值



反省:

这道题会卡vector的常数倍时间,通过这道题学会了手写邻接链表


#include <iostream>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string>
#include <vector>
#include <list>
#include <map>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <numeric>
#include <functional>
#define maxn 100005
#define maxe 200005

using namespace std;
int si,head[maxn];
int totalson[maxn],used[maxn],mostw[maxn];
struct edge{int to,next;}G[maxe];
bool flag;

void add_edge(int u,int v)
{
    G[si].to=v;
    G[si].next=head[u];
    head[u]=si++;
}

int dfs(int fa,int fafa)
{
    if(used[fa]==true){
        flag=false;
        return 0;
    }
    used[fa]=true;
    int s,son,ans=0;
    int tmp=0,sum=0;
    for(int i=head[fa];i!=-1;i=G[i].next){
        int son=G[i].to;
        if(son!=fafa){
            tmp=dfs(son,fa);
            ans=max(ans,tmp);
            sum+=tmp;
        }
    }
    mostw[fa]=ans;  //该节点所有子孙的最大值
    totalson[fa]=sum+1;   //该节点所有子孙与其自身的个数总和

    return sum+1;
}

int main()
{
    int n,m;
    while(scanf("%d %d",&n,&m)!=EOF){
        int u,v;
        memset(head,-1,sizeof(head));
        memset(totalson,0,sizeof(totalson));
        memset(mostw,0,sizeof(mostw));
        si=0;
        flag=true;
        for(int i=0;i<m;i++){
            scanf("%d %d",&u,&v);
            add_edge(u,v);
            add_edge(v,u);
        }
        memset(used,0,sizeof(used));
        dfs(1,0);
        for(int i=1;i<=n;i+=1){
            mostw[i]=max(mostw[i],n-totalson[i]);
        }
        int maxp=999999999,ans=-1;
        for(int i=1;i<=n;i+=1){
            if(maxp>mostw[i]){
                maxp=mostw[i];
                ans=i;
            }
        }
        printf("%d\n",ans);
    }

    return 0;
}




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值