http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=52099
有三只球队A B C,一共有n场比赛,然后现在进行了k场,不知道输赢,只知道A队伍和B队伍的胜场差是d1,B队伍和C队伍的胜场差是d2,
问这样比赛是不是可能出现平局,注意每一场比赛没有平局。
首先减枝:如果n%3 != 0,肯定不会使得三个队伍平手,输出“no”。
减枝后判断是否可能平局,需要进行两次检查:
一、第一次检查就是对于前k场比赛的结果分配是否合法。
怎样合法呢,就是最少的赢的数量是0次,因为对于每一局比赛,都会有一个赢的队伍,将这个1分配给A、B或C队,不会有0次以下的胜场数。
那么就设赢的最少的队伍数是x,其他的队伍是在这个基础上加d1、d2的组合
合法的条件就是x大于等于0,并且三个队伍赢的总和是3x+f(d1, d2),减去f(d1, d2)后是3的倍数。
d1,d2是差的绝对值,所以有四种情况:
d1>0 && d2>0 || d1>0 && d2<0 || d1<0 && d2>0 || d1<0 && d2<0
1、d1>0 && d2>0 最小的是C 设三个队胜场分别为x+d1+d2,x+d2,x
2、d1>0 && d2<0 最小的是B 设三个队胜场分别为 x+d1,x,x+d2
3、d1<0 && d2>0
这个要分析:
最大的是B无疑问,但最小的是A还是C取决于d1和d2的大小关系,
A=B-d1,C=B-d2
依然设最小的是x,B的值就是x+max(d1, d2),另外一个队的值就是x+abs(d1-d2)
之后在第二步抹平的时候也很简单:
最小的这个抹平方式就是加上他和B的差距max(d1, d2),
另一个队抹平的方式就是加上max(d1, d2)-[max(d1, d2)-min(d1, d2)] = min(d1, d2)
所以总共的抹平就是d1+d2
4、d1<0 && d2<0最小的是A 设三个队胜场分别为 x,x+d1,x+d1+d2
二、第二次检查就是将剩余的n-m场分配到胜场最少的、胜场次少的队伍上使胜场相同,之后有多余的场数就给3个队伍平均分配。
所以就是将第一步已经找出的3个队伍胜场的排列抹平,然后判断剩余的场数能否被3整除即可。
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 int main(){ 5 int T; 6 ll n,m,d1,d2; 7 scanf("%d",&T); 8 while (T--){ 9 scanf("%I64d%I64d%I64d%I64d",&n,&m,&d1,&d2); 10 ll s,d,s1,s2,s3,D1,D2,d3,k=n-m; 11 d=m-(d1+2*d2); 12 s=k-(2*d1+d2); 13 D1=m-(d1+d2); 14 s1=k-(max(d1,d2)+abs(d1-d2)); 15 D2=m-(max(d1,d2)+abs(d1-d2)); 16 s2=k-(d1+d2); 17 d3=m-(2*d1+d2); 18 s3=k-(d2*2+d1); 19 if ((s % 3==0 && s>=0) && (d>=0 && d % 3==0)) puts("yes");//d1>0 && d2>0 20 else if ((s1 % 3==0 && s1>=0) && (D1>=0 && D1 % 3==0)) puts("yes");//d1>0 && d2<0 21 else if ((s2 % 3==0 && s2>=0) && (D2>=0 && D2 % 3==0)) puts("yes");//d1<0 && d2>0 22 else if ((s3 % 3==0 && s3>=0) && (d3>=0 && d3 % 3==0)) puts("yes");//d1<0 && d2<0 23 else puts("no"); 24 } 25 return 0; 26 }