2018 “百度之星”程序设计大赛 - 初赛(B) - (1001,1004,1006)

吐槽CSDN,这篇博客我编辑了半天,发表之后竟然没变,感觉像是日了狗了,强迫症的我当然要再写一遍。

比赛链接:http://bestcoder.hdu.edu.cn/contests/contest_show.php?cid=826      

1001:HDU6380

1004:HDU6383

1006:HDU6385

bestcoder首页的官方题解简直是业界良心,建议作为参考:http://bestcoder.hdu.edu.cn/

 

1001 degree

题意:给定一个有 N 个点以及 M 条边的无向简单图,此图中保证没有任何圈 (cycle) 存在,也就是有多棵树的深林。

现在你可以对此图依序进行以下的操作:

  1. 移除至多 K条边。
  2. 在保持此图是没有圈的无向简单图的条件下,自由的添加边至此图中。

问最后此图中度数 (degree) 最大的点的度数可以多大呢?

解析:首先考虑有多个联通分量,此时找到度数最大的节点V0,发现可从V0每对一个联通分量连一条新边(连到任意点)就能使度数加一且不产生圈。

将多个两桶分量连成一个后,发现没有和V0直接相连的那些边每删除一条就形成一个新的联通分量,就能再连一次使V0度数加1。

代码

//2018 “百度之星”程序设计大赛 - 初赛(B)- 1001
#include 
using namespace std;
typedef long long ll;
const ll MAXN=2*1e5+5;

int N,M,K,num[MAXN];//num[i]存放点i的度数
int a[MAXN],b[MAXN];

//并查集
int father[MAXN];
int Find(int i)
{
    int t=father[i];
    while(i!=father[i])
    {
        i=father[i];
    }
    father[t]=i;
    return i;
}
void Union(int i,int j)
{
    int f1=Find(i),f2=Find(j);
    if(f1!=f2)
        father[f1]=f2;
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d",&N,&M,&K);
        for(int i=0;i<=N;i++)
        {
            num[i]=0;
            father[i]=i;
        }
        int maxp=0;//记录度数最大的点
        for(int i=1;i<=M;i++)
        {
            scanf("%d%d",&a[i],&b[i]);
            Union(a[i],b[i]);
            num[a[i]]++;
            num[b[i]]++;
            if(num[a[i]]>num[maxp]) maxp=a[i];
            if(num[b[i]]>num[maxp]) maxp=b[i];
        }
        int ans=num[maxp];
        set s;
        for(int i=0;i=ne)
            ans+=ne;
        else
            ans+=K;
        printf("%d\n",ans);
    }
    return 0;
}

 

1004 p1m2

题意:一个整数数组为稳定的,当且仅当同时符合以下两个条件:

  1. 数组里面的元素都是非负整数。
  2. 数组里面最大的元素跟最小的元素的差值不超过 1。

对数组有一种操作,任取两个元素,对其中一个-2,对另一个+1。现在给定一个整数数组a[],在任意进行操作后,请问在所有可能达到的稳定数组中,拥有最大的『数组中的最小值』的那些数组,此值是多少。

解析:看到“最大的最小”,就想到二分来做。我们二分枚举答案为x,对于大于x的a[i]必须每次-2减到值为x或x+1,那么它必须减(a[i]-x)/2次;对于小于x的a[i]必须每次+1加到值为x,那么它必须加(x-a[i])次,而且它可以再+1加到x+1(也可以不加)。

二分时:

  • 如果总的可以加的次数能够满足必须减的次数,令l=mid,枚举更大的x。
  • 如果必须减的次数小于必须加的次数,枚举更小的x。
  • 其他情况,枚举更大的x。

代码来自https://blog.csdn.net/qq_37868325:

#include
using namespace std;
const int inf = 1000000000;
long long a[500500],b,n,T;
long long sum[500500];
int dfs(long long rt)
{
    long long p=n+1,x,y=0;
    for(int i=1; i<=n; i++)
    {
        if(a[i]>rt)
        {
            p=i;
            break;
        }
    }
    x=rt*(p-1)-sum[p-1];
    for(int i=n; i>=p; i--)
    {
        y+=(a[i]-rt)/2;
    }
    //x是必须加的次数,y是必须减的次数,x+p-1是可以加的次数
    if(x<=y&&y<=x+p-1) return 1;
    if(y
1006 rect

题意:有一个大小为 MX×MY 的矩形,左下角坐标为 (0, 0),右上角坐标为 (MX, MY)。此矩形内有 N 个整数坐标的点 (x_i, y_i),x_i​​ 彼此不重复,y_i​​ 彼此也不重复。

现在要从每一个点画出一条线段,满足下列条件:

  • 线段起点为坐标点,终点在矩形范围的四个边界之一上。
  • 线段彼此不能交叉。

现在要让画出的线段的长度总和最小,请输出这个最小的长度总和值。

解析:每个点的长度最小值就是到4个边界距离的最小值,由于坐标不重合,枚举任意两个点可以发现,一个点最短距离的选择不会影响另一个的选择,所以求个和就ok。

代码

//2018 “百度之星”程序设计大赛 - 初赛(B)- 1006
#include 
using namespace std;
typedef long long ll;
const ll MAXN=1e5+5;

int mx,my,n;
int x,y;

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d",&mx,&my,&n);
        ll ans=0;
        while(n--)
        {
            scanf("%d%d",&x,&y);
            int tmp=min(x,y);
            tmp=min(tmp,mx-x);
            tmp=min(tmp,my-y);
            ans+=(ll)tmp;
        }
        printf("%I64d\n",ans);
    }
    return 0;
}

 

 

你可能感兴趣的:(ACM比赛练习)