字符串customers只包含’Y’和’N’两种字母, 表示 i 时刻商店是否有客人来。
如果商店在 i 时刻处于开门状态,'Y’的代价是0,'N’的代价是1.(开门了却没有客人就算损失)。
反之,在 i 时刻处于关门状态,'N’的代价是0,'Y’的代价是1.(关门却有客人来也是损失)。
如果商店在 j 时刻关门,那么从 j 时刻到最后都是关门状态。
问在哪个时刻关门代价最小。
思路:
方法一:
先计算一直在关门状态(0时刻关门)的代价。
然后把关门的时间右移,这时候只需要在上一步代价的基础上改变前一时刻的代价值。
举个例子,看Example1.
先计算0时刻关门的代价为3.
然后关门时刻移动到时刻1,这时时刻1后面还是关门状态,代价上没有变化,有变化的是0时刻的关门状态变成了开门状态。
这时候需要修改0时刻的代价,0时刻是’Y’, 关门时代价是1,开门代价是0,关门到开门状态需要减去代价1.
这时在上一步代价3的基础上减去多出来的代价1. 所以时刻1的代价为3-1=2.
后面依次类推。
直到移动到字符串最右边(边界外,表示一直开门)。
这个过程中记下最小代价和对应的时刻。
public int bestClosingTime(String customers) {char[] chs = customers.toCharArray();int cntY = 0;int cntN = 0;int n = chs.length;int[] penalty = new int[n+1];int minP = 0;int minIdx = 0;//从0时刻起不开门的代价for(int i = n-1; i >= 0; i--) {if(chs[i] == 'Y'){cntY ++;penalty[i] = penalty[i+1] + 1;} else {cntN ++;penalty[i] = penalty[i+1];}}if(cntY == n) return n;if(cntN == n) return 0;//i时刻关门minP = penalty[0];for(int i = 1; i <= n; i++) {if(chs[i-1] == 'Y') penalty[i] = penalty[i-1]-1;else penalty[i] = penalty[i-1]+1;if(penalty[i] < minP) {minP = penalty[i];minIdx = i;}}return minIdx;}
方法二:
其实不需要计算初始0时刻关门的代价。
因为它是多少并不影响结果。
还是Example1。我们把0~最后时刻关门的代价排列一下:
3, 2, 1, 2, 1
第2时刻代价最小,选在第2时刻关门。
这时,把初始时刻关门的代价由3改到0,后面还是和方法1一样,改变前一时刻的代价,于是得到
0, -1, -2, -1, -2
结果还是第2时刻代价最小。所以初始0时刻关门代价是多少都无所谓,只要后面能得到代价最小的时刻即可。
省去方法一中计算0时刻关门的代价,默认0时刻关门代价是0.
然后右移关门时刻 i , 每移一步,改变前一时刻的代价。比如移到1时,改变0时刻的代价。
0时刻是’Y’, 关门代价是1,开门代价是0,现在开门了需要把代价-1,反之如果是’N’就+1.
右移的过程中记录下最小代价和对应的时刻。
public int bestClosingTime(String customers) {char[] a = customers.toCharArray();int n = a.length;int j = -1, penalty = 0, minP = 0;for (int i = 0; i < a.length; i++) {penalty += a[i] == 'Y'? -1 : 1;if (penalty < minP) {j = i;minP = penalty;}}return j + 1;}