HDU 5353 Average(贪心)

142 篇文章 0 订阅

Description
有n个人组成一个环,相邻的两个人能互相给糖果,对于相邻的两个人而言,只能进行一次操作,要么x给y一个糖果,要么y给x一个糖果,要么不动,问能否经过一定的操作使得每个人的糖果数一样,并输出步骤
Input
第一行为用例组数T,每组用例第一行为人数n,第二行为n个整数表示这n个人手中的糖果数
Output
对于每组用例,如果存在一种方案可以使得所有人手中的糖果数相同则输出YES并输出步骤,每部步骤由a b组成表示a给b一颗糖,如果不需要任何步骤则输出0,如果不存在合法方案则输出NO
Sample Input
3
6
1 0 1 0 0 0
5
1 1 1 1 1
3
1 2 3
Sample Output
NO
YES
0
YES
2
2 1
3 2
Solution
首先累计所有人的糖果数量并找到拥有糖果数最多的一个人,然后求出平均每个人应有的糖果数,如果总数不能整除人数直接输出NO即可;如果最多糖果数等于均值则直接输出YES 0即可;如果最多糖果数比均值多2及以上那么这个人最少要给旁边人两颗糖以上,这显然不可能,所以可以直接输出NO;当最多糖果数比均值大一或大二,那么这个人必须给旁边人糖,我们可以枚举两种情况分别是给左边和给右边判断是否有可行解,以给左边为例,如果这个人给了他左边人一颗糖,那么这两个人之间不会再进行其他操作,那么他左边的人只会和他左边第二个人进行操作,这样就把操作给定了方向,每个人为了使自己到达均值只能和其左边的人进行操作,这样对于任意一个人,只要他的糖果数比均值大二及以上或者小二及以上,那么这个方向传递就无解,向右传同理,这样一来两个方向枚举判断是否存在可行解并记录步骤最后输出即可
Code

#include<cstdio>
#include<iostream>
using namespace std;
#define maxn 111111
int a[maxn],b[maxn];
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n;
        scanf("%d",&n);
        long long sum=0;
        int av=0,maxx=0;
        for(int i=0;i<n;i++)
        {
            scanf("%d",&a[i]);
            b[i]=a[i];
            sum+=a[i];//累加糖果数 
            if(a[i]>a[maxx])//找到拥有糖果数最多的那个人 
                maxx=i;
        }
        if(sum%n!=0)//总数不能整数人数,无解 
        {
            printf("NO\n");
            continue;
        }
        av=sum/n;//均值 
        if(a[maxx]>av+2)//最大值超过均值2及以上,无解 
        {
            printf("NO\n");
            continue;
        }
        if(a[maxx]==av)//最大值等于均值,那么所有人的糖果数都等于均值,有解且不用进行任何操作 
        {
            printf("YES\n0\n");
            continue;
        }
        int flag1=1,flag2=1;//标记两个方向是否存在可行解 
        int res=0;
        pair<int,int>ans[maxn];//记录步骤,第一键值给第二键值糖 
        //向右传 
        for(int i=0,j=maxx;i<n;i++,j=(j+1)%n)
        {
            int k=(j+1)%n;
            if(b[j]==av+1|| j==maxx&&b[j]==av+2)//如果当前人多一颗糖或者当前人是拥有糖果数最多的人且比均值多两颗那么这个人必须给下一个人一颗糖 
            {
                b[j]--,b[k]++;//这个人给下一个人一颗糖 
                //记录该步骤 
                ans[res].first=j;
                ans[res++].second=k;
            }
            else if(b[j]==av)//当前人糖果数等于均值不用操作 
                continue;
            else if(b[j]==av-1)//当前人少一颗糖则必须从下一个人手中拿一颗糖 
            {
                b[j]++,b[k]--;//下一个人给这一个人一颗糖
                //记录该步骤 
                ans[res].first=k;
                ans[res++].second=j;
            }
            else//这个人的糖果数超过合理范围 
            {
                flag1=0;//改变标记变量的值 
                res=0;//步骤数清零 
                break;//退出循环 
            }
        }
        if(flag1)//向右串有可行解直接输出进行下一组用例 
        {
            printf("YES\n");
            printf("%d\n",res);
            for(int i=0;i<res;i++)
                printf("%d %d\n",ans[i].first+1,ans[i].second+1);
            continue;
        }
        //向右传不行就向左传 
        for(int i=0,j=maxx;i<n;i++,j=(j+n-1)%n)
        {
            int k=(j+n-1)%n;
            if(a[j]==av+1|| j==maxx&&a[j]==av+2)//如果当前人多一颗糖或者当前人是拥有糖果数最多的人且比均值多两颗那么这个人必须给上一个人一颗糖 
            {
                a[j]--,a[k]++;//这个人给上一个人一颗糖
                //记录该步骤 
                ans[res].first=j;
                ans[res++].second=k;
            }
            else if(a[j]==av)//当前人糖果数等于均值不用操作 
                continue;
            else if(a[j]==av-1)//当前人少一颗糖则必须从上一个人手中拿一颗糖 
            {
                a[j]++,a[k]--;//上一个人给这一个人一颗糖
                //记录该步骤 
                ans[res].first=k;
                ans[res++].second=j;
            }
            else//这个人的糖果数超过合理范围  
            {
                flag2=0;//改变标记变量的值 
                res=0;//步骤数清零 
                break;//退出循环
            }
        }
        if(flag2)//向左传有可行解 
        {
            printf("YES\n");
            printf("%d\n",res);
            for(int i=0;i<res;i++)
                printf("%d %d\n",ans[i].first+1,ans[i].second+1);
            continue;
        }
        printf("NO\n");//向两边传都没可行解则无解 
    }
    return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值