HDU 5353 Average(这里或许有你一直WA的原因)

Average

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)
Special Judge

Problem Description
There are  n  soda sitting around a round table. soda are numbered from  1  to  n  and  i -th soda is adjacent to  (i+1) -th soda,  1 -st soda is adjacent to  n -th soda.

Each soda has some candies in their hand. And they want to make the number of candies the same by doing some taking and giving operations. More specifically, every two adjacent soda  x  and  y  can do one of the following operations only once:
1.  x -th soda gives  y -th soda a candy if he has one;
2.  y -th soda gives  x -th soda a candy if he has one;
3. they just do nothing.

Now you are to determine whether it is possible and give a sequence of operations.
 

Input
There are multiple test cases. The first line of input contains an integer  T , indicating the number of test cases. For each test case:

The first contains an integer  n   (1n105) , the number of soda.
The next line contains  n  integers  a1,a2,,an   (0ai109) , where  ai  denotes the candy  i -th soda has.
 

Output
For each test case, output "YES" (without the quotes) if possible, otherwise output "NO" (without the quotes) in the first line. If possible, then the output an integer  m   (0mn)  in the second line denoting the number of operations needed. Then each of the following  m  lines contain two integers  x  and  y   (1x,yn) , which means that  x -th soda gives  y -th soda a candy.
 

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
 

Source
 
/******************************************************************************/
题意:n个人围成一圈,每个人手里都有一定数量的糖果,问经过怎样的操作能够使得最终每个人手里的糖果数量相同,如果无解,输出“NO”。
相邻的两个人之间操作分为三种:
①第i个人可以给第i+1个人一颗糖果;
②第i+1个人可以给第i个人一颗糖果;
③他们之间什么都不做,即不给相邻的人糖果。
相邻的两个人之间只能进行一次上述的一种操作。
解题报告:多校的时候,把这个题目想得复杂了,一个人既可以向右边的人进行一种操作,又可以向左边的人进行一中操作着实有点难搞,比如说第i个人先在手里的糖果数比最终平均每人手里的糖果数多1,此时就得考虑该把多出的1颗糖果给哪边合适,若相邻两边的糖果数一样,即第i-1个人和第i+1个人手里的糖果数都比平均值少1,那就不知道该把这颗糖给谁了。

后来浙大出题人的题解报告出来以后,才发现此题不需要考虑得这么复杂,先放上出题人的解题报告


虽然每个人既可以对他右边的人进行操作,又可以对他左边的人操作,但是因为他们围成了一个圈,你只要考虑一个人的一个方向,自然能够将左右两边的操作都囊括。所以将每个人现有的糖果数减去平均数之后,我们只需枚举第一个人对第二个人的三种操作,就可以得到最终解,因为该人只有这三种可能性。接着向右遍历一遍判断每个人的状态即可。比如说第i个人此时的差值为1,说明他手上多了一颗糖果,因为左边是已经操作过的人,所以自然将糖果给右边;若差值为-1,则代表缺一颗糖果,左边是操作过的,所以向右边拿一颗糖果;若差值为0的话则满足条件不再需要进行操作。另外,如果差值是除了这三个数以外的数一律输出“NO”,因为每两个人之间只能进行一次操作,而一次操作又只能给1颗糖,所以差值大于1颗糖的都是无解的。

另外,若起始每个人的糖果数之和没法被n整除也是无解的,即无法均分的情况。

在这里我要提一下一种导致WA的原因,即起始每个人手中的糖果数已经相同。这样的话已满足题目要求,输出“YES”和“0”即可。但是,有些人(包括我自己)可能会有另一种操作使得最终每个人手中的糖果数相同,即每个人都给右边的人一颗糖果,这样循环一圈也是满足条件的。然而,你如果这样写的话,恭喜你,收获到WA一枚。

例如

5

1 1 1 1 1

正确结果是

YES

0

理论上可行,但实际会WA的结果是

YES

1 2

2 3

3 4

4 5

5 1

好了,闲话不多说,上代码

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<queue>
#include<math.h>
#include<vector>
#include<map>
#include<set>
#include<cmath>
#include<string>
#include<algorithm>
#include<iostream>
#define exp 1e-10
using namespace std;
const int N = 100005;
const int inf = 1000000000;
const int mod = 1000000007;
int s[N],s1[N],s2[N],n,u[N],v[N],k;
bool fun(int *s)
{
    int i;
    for(i=2;i<=n;i++)
        if(s[i]==-1)
        {
            s[i%n+1]--;
            u[k]=i%n+1;
            v[k++]=i;
        }
        else if(s[i]==1)
        {
            s[i%n+1]++;
            u[k]=i;
            v[k++]=i%n+1;
        }
        else if(s[i])
            break;
    if(i<=n||s[1])
        return false;
    return true;
}
void Clear()
{
    memset(u,0,sizeof(u));
    memset(v,0,sizeof(v));
    k=0;
}
bool all_same()
{
    for(int i=1;i<=n;i++)
        if(s[i]!=s[1])
            return false;
    return true;
}
int main()
{
    int t,i;
    __int64 sum;
    bool flag;
    scanf("%d",&t);
    while(t--)
    {
        sum=0;flag=false;
        scanf("%d",&n);
        for(i=1;i<=n;i++)
        {
            scanf("%d",&s[i]);
            sum+=s[i];
        }
        if(all_same())
        {
            puts("YES\n0");
            continue;
        }
        if(sum%n)
        {
            puts("NO");
            continue;
        }
        for(sum/=n,i=1;i<=n;i++)
        {
            s[i]-=sum;s1[i]=s2[i]=s[i];
            if(s[i]>2||s[i]<-2)
                break;
        }
        if(i<=n)
        {
            puts("NO");
            continue;
        }
        Clear();
        s2[2]++;s2[1]--;
        u[k]=1;
        v[k++]=2;
        if(!fun(s2))
        {
            Clear();
            s1[2]--;
            s1[1]++;
            u[k]=2;
            v[k++]=1;
        }
        else
            flag=true;
        if(!flag)
        {
            if(!fun(s1))
                Clear();
            else
                flag=true;
        }
        if(!flag)
            if(fun(s))
                flag=true;
        if(flag)
        {
            puts("YES");
            printf("%d\n",k);
            for(i=0;i<k;i++)
                printf("%d %d\n",u[i],v[i]);
        }
        else
            puts("NO");
    }
    return 0;
}
欢迎大家指点
菜鸟成长记

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值