zoukankan      html  css  js  c++  java
  • CodeForces 1292C Xenon's Attack on the Gangs

    题目描述

      

           

    输入格式

          

    输出格式

       

    思路分析:

      这道题的题意简单来说就是两点之间的边权就是从0到n-1的值之中,从u到v到唯一路径(因为是树)不经过的值之中的最小值,题目最后要求算出从所有一点到所有另一点的所有权值之和的最大值。显然我们可以贪心,我们让权值小的边尽可能多的在任意两点之间的线段上,这样就能使最多的边的边权之和尽可能大。由于0最小,我们便把它安排在图的中央,则可以使两边的点互通使都过0(原因:0的这条边将图分为两部分,设这个图共有n个点,0将图分为大小为x和y的两部分,则互通过0的边数为x*y,则我们要求x*y的最大值,根据数学知识可得当x尽可能等于y时x*y最大,即0尽可能在图的中央)在安排完0之后,我们开始思考下一条边的位置,还是刚才括号内的原因,故越小的边越应该往中间凑,故1应该在0的旁边。

    看一下从liuchang大佬那里白嫖的图

      我们先跑n遍DFS,用sum[i][u]求出以i为根时u节点的子树大小,用pa[i][v]表示以i为根时v节点的父亲,先把0放在中央,权值为0的边的两边元素分别为u,v,则此时sum[u]*sum[v]个边同时过0,此时它们的权值都为1(1尚未进入图中),即答案值加上sum[u]*sum[v],之后我们既可以把1接在(u,pa[u][v])间,也可在  (v,pa[v][u])之间,此时sum[u]*sum[v]的部分边的权值由于过了1而发生改变,变为了2,由于它们的权值已经是1,最终结果再加上一个sum[u]*sum[v](u,v均已变为权值为1的两端节点),这相当与我们把大小n的大问题拆为了大小x和大小y的子问题,于是在统计答案时想到递归处理.最后记得要开long long

    附上代码

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 using namespace std;
     5 const int N=5e3+10;
     6 typedef long long ll;
     7 int Head[N],tot,n,m;
     8 struct Node{
     9     int next,to;
    10 }edge[N];
    11 void Add(int x,int y){
    12     edge[++tot].to=y;
    13     edge[tot].next=Head[x];
    14     Head[x]=tot;
    15 }
    16 ll dp[N][N];  //dp最终答案可能很大,要开long long 
    17 int sum[N][N],pa[N][N];
    18 void dfs(int rt,int u,int fa){  //初始化pa,sum 
    19     sum[rt][u]=1;
    20     for(int i=Head[u];i;i=edge[i].next){
    21         int v=edge[i].to;
    22         if(v==fa) continue;
    23         dfs(rt,v,u);
    24         sum[rt][u]+=sum[rt][v];
    25         pa[rt][v]=u;
    26     }
    27 }
    28 ll Dp(int u,int v){   //递归进行答案计算 
    29     if(u==v) return 0;
    30     if(dp[u][v]) return dp[u][v];
    31     return dp[u][v]=max(Dp(u,pa[u][v]),Dp(v,pa[v][u]))+sum[u][v]*sum[v][u];
    32 }
    33 int main(){
    34     scanf("%d",&n);
    35     for(int i=1;i<n;++i){
    36         int x,y;
    37         scanf("%d%d",&x,&y);
    38         Add(x,y);Add(y,x);
    39     }
    40     for(int i=1;i<=n;++i){
    41         dfs(i,i,0);  //分别初始化每一个点 
    42     }
    43     ll ans=-1;
    44     for(int i=1;i<=n;++i){
    45         for(int j=1;j<=n;++j)
    46             ans=max(ans,Dp(i,j)); //寻找答案 
    47     }
    48     printf("%lld
    ",ans);
    49     return 0;
    50 }
  • 相关阅读:
    LeetCode#18-四数之和
    LeetCode#209-长度最小的子数组
    LeetCode#234-回文链表
    LeetCode#287-寻找重复数
    LeetCode#167-两数之和
    LeetCode#141-环形链表
    LeetCode#826-安排工作达到最大收益
    LeetCode#86-分隔链表
    LeetCode#19-删除链表的倒数第N个节点
    LeetCode#88-合并两个有序数组
  • 原文地址:https://www.cnblogs.com/li-jia-hao/p/12680446.html
Copyright © 2011-2022 走看看