力扣LCP 46. 志愿者调配随笔
"想象力比知识更重要,因为知识是有限的,而想象力概括了世界上的一切,推动着社会进步,并且是知识进步的源泉。"——爱因斯坦
题目
「力扣挑战赛」有 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
; - 测试数据保证给定的场馆间的道路分布情况中不会出现自环、重边的情况。
难度:中等
分析
笔者似乎是第一次接触这种题,知道怎么手算,但是不知道代码怎么写😓。
由于题目给出了最后一天除了第0号场馆的各场馆人数,可以将第0号场馆的人数设为未知数x,从最后一天往前递推(加改为减,减改为加,除半改为乘半),可以得到最开始所有场馆的人数(使用未知数表示)。根据最开始的人数总和,求解一元一次方程,即可得到未知数的值并计算得到初始所有场馆的人数。我们可以把初始各场馆的人数用ax+b的形式表示,其中a表示系数,b表示常数,即使用两个数组存储系数和常数。在每一步逆推时,都需要对系数和常数进行操作,相加得到系数和常数的总和。求解方程at*x+bt=totalNumber,即x=(totalNumber-bt)/at,最终计算每个场馆的人数。
解答
class Solution {public int[] volunteerDeployment(int[] finalCnt, long totalNum, int[][] edges, int[][] plans) {int n=finalCnt.length+1;List<List<Integer>> links=new ArrayList<>();for (int i=0;i<n;i++){links.add(new ArrayList<>());}for (int[] e:edges){links.get(e[0]).add(e[1]);links.get(e[1]).add(e[0]);}long[] xishu=new long[n];long[] changshu=new long[n];xishu[0]=1L;for (int i=1;i<n;i++){changshu[i]=1L*finalCnt[i-1];}for (int i=plans.length-1;i>=0;i--){int idx=plans[i][1];int op=plans[i][0];if (op==1){xishu[idx]<<=1;changshu[idx]<<=1;}else{for (int j:links.get(idx)){if (op==2){xishu[j]-=xishu[idx];changshu[j]-=changshu[idx];}else{xishu[j]+=xishu[idx];changshu[j]+=changshu[idx];}}}}long xt=0L;for (long i:xishu){xt+=i;}long ct=0L;for (long i:changshu){ct+=i;}long x=(totalNum-ct)/xt;int[] ans=new int[n];for (int i=0;i<n;i++){ans[i]=(int)(xishu[i]*x+changshu[i]);}return ans;}
}
“聪明人变成了痴愚,是一条最容易上钩的游鱼;因为他凭恃才高学广,看不见自己的狂妄。”——莎士比亚