题意:
有n个人组成一个环,相邻的两个人能互相给糖果,对于相邻的两个人而言,只能进行一次操作,要么x给y一个糖果,要么y给x一个糖果,要么不动,问能否经过一定的操作使得每个人的糖果数一样,并输出步骤。
解析:
很显然,如果当前的和sum不能被n除尽,那么肯定不能均分所以这种情况是”NO”。
然后可以计算出每个人的糖果,如果某人糖果和平均值的差距的绝对值>=2,那么也不可能平均分配。因为每条边只能走一次,而且每次只能给一个糖果,如果不管怎么索要,怎么给予,至多给2个,所以差距的绝对值>=2这种情况也是非法的。
然后就是判断环的情况,找到第一个> 0的人让他分发糖果,这时可以朝着前后两个方向走,两个方向都判断一次,如果第一个是>=2的,那么就分配给后面一个人一个糖果。
枚举前一个人对后一个人的三种操作,
然后这样第i个人必然对第i+1个人按照
1.少一个糖果就从下一个人手中拿一个
2.多一个就给一个给下一个人
然后判断最后的差距是否都是=0的,如果不是这种情况是false的。
my code
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <vector>
#define pb push_back
typedef long long ll;
using namespace std;
const int N = 1e5 + 10;
struct Node {
int a, b;
Node(int a, int b) : a(a), b(b) {}
};
vector<Node> ans;
int n;
ll sum;
int A[N], a[N];
bool run(int dir, int st) {
ans.clear();
memcpy(a, A, sizeof(A));
int flag = 0;
int u, v;
if(a[st] == 2) {
u = st; v = (st - dir + n) % n;
a[u]--, a[v]++;
ans.pb(Node(u, v));
flag = 1;
}
for(int i = 0; i < n - flag; i++) {
u = (st + i*dir + n) % n;
v = (st + (i+1)*dir + n) % n;
if(a[u] > 0) {
a[u]--, a[v]++;
ans.pb(Node(u, v));
}else if(a[u] < 0) {
a[u]++, a[v]--;
ans.pb(Node(v, u));
}
if(abs(a[v]) >= 2) return false;
}
for(int i = 0; i < n; i++) {
if(a[i]) return false;
}
return true;
}
bool solve() {
sum /= n;
for(int i = 0; i < n; i++) A[i] -= sum;
for(int i = 0; i < n; i++)
if(abs(A[i]) > 2)
return false;
if(n == 2 && abs(A[0]) >= 2) {
return false;
}
for(int i = 0; i < n; i++) {
if(A[i] > 0) {
if(run(-1, i)) return true;
if(run(1, i)) return true;
return false;
}
}
return true;
}
int main() {
int T;
scanf("%d", &T);
while(T--) {
ans.clear();
sum = 0;
scanf("%d", &n);
for(int i = 0; i < n; i++) {
scanf("%d", &A[i]);
sum += A[i];
}
if(sum % n != 0) {
puts("NO");
continue;
}
if(!solve()) {
puts("NO");
}else {
puts("YES");
printf("%d\n", (int)ans.size());
for(int i = 0; i < ans.size(); i++)
printf("%d %d\n", ans[i].a+1, ans[i].b+1);
}
}
return 0;
}