题目描述:
给出两个元素数量相等的数组,一个a数组,一个b数组,你可以从a数组中任意选择一个数(ai)- 1,一个数(aj)+1。目标是将a数组变成b数组。操作次数不限制。
输入:
第一行:T个测试样例(T <= 100)
第二行:数组的元素数量(N <= 100)
第三行:a数组
第四行:b数组
输出:
若对a数组怎么操作也不能变成b数组,则输出-1(有解);
若有解,则输出路径;
样例:
输入:
4 4 1 2 3 4 3 1 2 4 2 1 3 2 1 1 0 0 5 4 3 2 1 0 0 1 2 3 4
输出:
2 2 1 3 1 -1 0 6 1 4 1 4 1 5 1 5 2 5 2 5
注解:
对于第一个样例:(下标是从1开始的)
i=2, j=1: [1,2,3,4]→[2,1,3,4];
i=3, j=1: [2,1,3,4]→[3,1,2,4];
思路:
首先,先判断有没有解,因为是每次操作是选定两个元素,一个元素减1,一个元素加1,所以说有解的话,a中每个元素与b中对应的每个元素的差值(a[i] - b[i])的和应该为0,只有这样才能保证有解。
我们把a中每个元素和b中对应的元素的差值存到数组里,这样容易确定对a中哪个元素进行哪个操作:
若差值为正数,说明a的元素比b中对应元素大,那么a中的对应元素(ai)肯定要 -1;若差值为负数,说明a的元素比b中对应元素小,那么a中的对应元素(aj)要 + 1。
由于我们需要输出路径(a中哪个元素怎么变的),所以我们要找到某种方法来确保每一组输出的路径合理且正确。这种通法就是遍历c数组(操作次数就是差值为正数(或者负数)的个数,正数负数相抵消),第一个差值为整数的c[i]对应的a[i],让它-1,并用一个变量记录当前的i;第一个差值为负数的的c[i]对应的a[i] ,让它+1,,并用另一个变量记录当前的i。
AC代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#include <map>
using namespace std;
const int N = 1e2 + 10;
int a[N],b[N],c[N]; // c数组记录差值
int cnt; // 记录操作次数
int sum; // 判断有没有解
int main()
{
int t;
scanf("%d",&t);
while(t --)
{
sum = 0;
cnt = 0;
int n;
scanf("%d",&n);
for(int i = 1 ; i <= n ; i ++) scanf("%d",&a[i]);
for(int i = 1 ; i <= n ; i ++) scanf("%d",&b[i]);
//初始化c数组
for(int i = 1 ; i <= n ; i ++) c[i] = a[i] - b[i];
for(int i = 1 ; i <= n ; i ++) sum += c[i];
if(sum != 0)
{
printf("-1\n");
}
else
{
//求操作次数
for(int i = 1 ; i <= n ; i ++)
{
if(c[i] > 0) cnt += c[i];
}
printf("%d\n",cnt);
int l,r; // 记录动态的i,j值方便输出路径
for(int k = 1 ; k <= cnt ; k ++)
{
for(int i = 1 ; i <= n ; i ++)
{
if(c[i] > 0)
{
l = i;
a[i] --;
c[i] --;
break;
}
}
for(int i = 1 ; i <= n ; i ++)
{
if(c[i] < 0)
{
r = i;
a[i] ++;
c[i] ++;
break;
}
}
printf("%d %d\n",l,r);
}
}
}
return 0;
}