「力扣挑战赛」有 n
个比赛场馆(场馆编号从 0
开始),场馆之间的通道分布情况记录于二维数组 edges
中,edges[i]= [x, y]
表示第 i
条通道连接场馆 x
和场馆 y
(即两个场馆相邻)。初始每个场馆中都有一定人数的志愿者(不同场馆人数可能不同),后续 m
天每天均会根据赛事热度进行志愿者人数调配。调配方案分为如下三种:
- 将编号为
idx
的场馆内的志愿者人数减半; - 将编号为
idx
的场馆相邻的场馆的志愿者人数都加上编号为idx
的场馆的志愿者人数; - 将编号为
idx
的场馆相邻的场馆的志愿者人数都减去编号为idx
的场馆的志愿者人数。
所有的调配信息记录于数组 plans
中,plans[i] = [num,idx]
表示第 i
天对编号 idx
的场馆执行了第 num
种调配方案。
在比赛结束后对调配方案进行复盘时,不慎将第 0
个场馆的最终志愿者人数丢失,只保留了初始所有场馆的志愿者总人数 totalNum
,以及记录了第 1 ~ n-1
个场馆的最终志愿者人数的一维数组 finalCnt
。请你根据现有的信息求出初始每个场馆的志愿者人数,并按场馆编号顺序返回志愿者人数列表。
注意:
- 测试数据保证当某场馆进行第一种调配时,该场馆的志愿者人数一定为偶数;
- 测试数据保证当某场馆进行第三种调配时,该场馆的相邻场馆志愿者人数不为负数;
- 测试数据保证比赛开始时每个场馆的志愿者人数都不超过
10^9
; - 测试数据保证给定的场馆间的道路分布情况中不会出现自环、重边的情况。
示例 1:
输入:finalCnt = [1,16], totalNum = 21, edges = [[0,1],[1,2]], plans = [[2,1],[1,0],[3,0]]
输出:
[5,7,9]
解释:
示例 2 :
输入:
finalCnt = [4,13,4,3,8], totalNum = 54, edges = [[0,3],[1,3],[4,3],[2,3],[2,5]], plans = [[1,1],[3,3],[2,5],[1,0]]
输出:
[10,16,9,4,7,8]
提示:
2 <= n <= 5*10^4
1 <= edges.length <= min((n * (n - 1)) / 2, 5*10^4)
0 <= edges[i][0], edges[i][1] < n
1 <= plans.length <= 10
1 <= plans[i][0] <=3
0 <= plans[i][1] < n
finalCnt.length = n-1
0 <= finalCnt[i] < 10^9
0 <= totalNum < 5*10^13
思路:本题可采用类似于解方程的方式求解,不过需要保证可实现。我们可以假定最终0号场馆有x名志愿者,此时我们将操作序列倒着执行一遍【不仅顺序上颠倒,同时保证之前的操作1变为乘2、操作2变为减操作、操作3变为加操作】,然后得到最初状态,此时n个场馆中是有未知数x的,但我们可以知道的是初始时总的志愿者数量为totalNum,于是可以列等式求出这个唯一的未知变量x,也就得到了最终形态各个场馆的志愿者数量。再倒着执行一遍操作序列便能得出最初志愿者数量。
实现:在实现时我们可以假设x为一个具体值,在得出最初数量时,和真实的数量进行比较,假设值加上比较得出的差值的和就是最终0号场馆的志愿者数量。【注意:我们需要记录下操作过程中未知量缩产生的系数变化,在求差值时需要除以该系数(小学一元一次方程求解问题,不再过多赘述)】。
class Solution {
private int cof = -1;
public int[] volunteerDeployment(int[] finalCnt, long totalNum, int[][] edges, int[][] plans) {
int m = edges.length;
int n = finalCnt.length + 1;
List<List<Integer>> e = new ArrayList<>();
for (int i = 0; i < n; i++)
e.add(new ArrayList<>());
for (int i = 0; i < m; i++) {
e.get(edges[i][0]).add(edges[i][1]);
e.get(edges[i][1]).add(edges[i][0]);
}
long[] now = new long[n];
now[0] = 0;
findAns(now, finalCnt, plans, e, n);
long sum = 0;
for (int i = 0; i < n; i++)
sum += now[i];
now[0] = (totalNum - sum) / cof;
findAns(now, finalCnt, plans, e, n);
int[] ans = new int[n];
for (int i = 0; i < n; i++)
ans[i] = (int) now[i];
return ans;
}
private void findAns(long[] now, int[] finalCnt, int[][] plans, List<List<Integer>> e, int n) {
int m = plans.length;
int[] num = new int[n];
num[0] = 1;
for (int i = 0; i < n - 1; i++)
now[i + 1] = finalCnt[i];
for (int i = m - 1; i >= 0; i--) {
if (plans[i][0] == 1) {
now[plans[i][1]] *= 2;
num[plans[i][1]] *= 2;
} else if (plans[i][0] == 2) {
for (int j = 0; j < e.get(plans[i][1]).size(); j++) {
int nei = e.get(plans[i][1]).get(j);
now[nei] -= now[plans[i][1]];
num[nei] -= num[plans[i][1]];
}
} else {
for (int j = 0; j < e.get(plans[i][1]).size(); j++) {
int nei = e.get(plans[i][1]).get(j);
now[nei] += now[plans[i][1]];
num[nei] += num[plans[i][1]];
}
}
}
if (cof == -1) {
cof = 0;
for (int i = 0; i < n; i++)
cof += num[i];
}
}
}