题目
某购物城有m个商铺,现决定举办一场活动选出人气最高店铺。活动共有n位市民参与,每位市民只能投一票,但1号店铺如果给该市民发放q元的购物补贴,该市民会改为投1号店铺。请计算1号店铺需要最少发放多少元购物补贴才能成为人气最高店铺(即获得的票数要大于其他店铺),如果1号店铺本身就是票数最高店铺,返回0。
输入描述:
第一行为小写逗号分割的两个整数n,m,其中第一个整数n表示参与的市民总数,第二个整数m代表店铺总数,1<= n,m <= 3000.
第2到n+1行,每行为小写逗号分割的两个整数p,q,表示市民的意向投票情况,其中每行的第一个整数p表示该市民意向投票给p号店铺,第二个整数q表示其改投1号店铺所需给予的q元购物补贴,1<= p <= m,1<=q <=10^9.不考虑输入的格式问题
输出描述
1号店铺需要最少发放购物补贴金额。
示例1
输入:
5,5
2,10
3,20
4,30
5,40
5,90
输出:
50
说明:
有5个人参与,共5个店铺。
如果选择发放10元+20元+30元=60元的补贴来抢2,3,4号店铺的票,总共发放了60元补贴(5号店铺有2票,1号店铺要3票才能胜出)
如果选择发放10元+40元=50元的补贴来抢2,5号店铺的票,总共发放了50元补贴(抢了5号店铺的票后,现在1号店铺只要2票就能胜出)
所以最少发放50元补贴
示例2
输入:
5,5
2,10
3,20
4,30
5,80
5,90
输出:
60说明:
有5个人参与,共5个店铺.
如果选择发放10元+20元+30元=60元的补贴来抢2,3,4号店铺的票,总共发放了60元补贴(5号店铺有2票,1号店铺要3票才能胜出)
如果选择发放10元+80元=90元的补贴来抢2,5号店铺的票,总共发放了90元补贴(抢了5号店铺的票后,现在1号店铺只要2票就能胜出)
所以最少发放60元补贴
思路
组合思路,列举所有的可能,输出满足条件的最少补贴。组合套路详见:【JAVA-排列组合】一个套路速解排列组合题
- 定义votes数组存放每个店铺的意向情况,其中votes[0]代表人气最高店铺的索引,votes[1]代表1号店铺的的票数量,在将输入存入votes时需注意:
当某个店铺的人气大于votes[0]店铺的人气(即votes[votes[0]]),将该店铺的索引存入votes[0];
当某个店铺的人气等于votes[0]店铺的人气且该店铺不是一号店铺时,也应该将该店铺的索引存入votes[0]。这样就能保证只有当votes[0]=1时,1号店铺是唯一的人气最高店铺。- 对于每行输入,转为id和price,存入实体VotePrice中,并加入到一个list,因为是要通过给list中的店铺发放补贴,从而将票数给到1号店铺,所以list中不存放1号店铺自身的信息。
选择组合时,应该考虑剪枝条件,比如对以下数据:3,5
3,10
4,25
2,25
2,90
不剪枝时,以下两种组合可能对会遍历一次:
3,5、4,25、2,25
3,10、4,25、2,25
很明显,第二次遍历是冗余情况,第一种花5的代价就能拉取到商铺3的投票,第二种却要10的代价。所以当组合中只选一个3时,选了5的代价就没必要选10。此时应该剪枝。list应该按照id,price升序排序
- 通过组合可以列举所有可能的情况,计算抢了某些店铺的票后,当前的votes中的人气最高的唯一店铺是否是1号店铺。比如,记当前抢的店铺为cur(list.get(i)):
那么需要更新人气: votes[1]++; votes[cur.getId()]–;
回溯移出时则相反: votes[1]–; votes[cur.getId()]++;- 最后检查votes中最高人气的唯一店铺(check方法)是否是1号店铺且补贴使用最少即可
因为最后需要输入的是最少补贴,即不关心具体有哪些组合,所以组合中可以删除中间的path变量
题解
package hwod;import java.util.*;public class VoteMall {public static void main(String[] args) {Scanner sc = new Scanner(System.in);String[] firstLines = sc.nextLine().split(",");int n = Integer.parseInt(firstLines[0]);int m = Integer.parseInt(firstLines[1]);int[] votes = new int[m + 1];List<VotePrice> list = new ArrayList<>();for (int i = 0; i < n; i++) {String[] lines = sc.nextLine().split(",");int id = Integer.parseInt(lines[0]);int price = Integer.parseInt(lines[1]);if (id != 1) list.add(new VotePrice(id, price));votes[id]++;if (votes[id] > votes[votes[0]] || (id != 1 && votes[id] == votes[votes[0]])) {votes[0] = id;}}System.out.println(voteMall(list, votes));}private static int res = Integer.MAX_VALUE;private static int voteMall(List<VotePrice> list, int[] votes) {Collections.sort(list);if (votes[0] == 1) return 0;//用于剪枝int[] used = new int[list.size()];dfs(list, 0, used, votes, 0);return res;}private static void dfs(List<VotePrice> list, int start, int[] used, int[] votes, int price) {if (check(Arrays.copyOfRange(votes, 1, votes.length))) {res = Math.min(res, price);return;}for (int i = start; i < list.size(); i++) {if (i > 0 && list.get(i - 1).getId() == list.get(i).getId() && used[i - 1] == 0) continue; //同层相同剪枝VotePrice cur = list.get(i);votes[1]++;votes[cur.getId()]--;used[i] = 1;dfs(list, i + 1, used, votes, price + cur.getPrice());votes[1]--;votes[cur.getId()]++;used[i] = 0;}}private static boolean check(int[] nums) {int maxIdx = 0;for (int i = 1; i < nums.length; i++) {if (nums[i] >= nums[maxIdx]) {maxIdx = i;}}return maxIdx == 0;}}class VotePrice implements Comparable<VotePrice> {private int id;private int price;public int getId() {return id;}public void setId(int id) {this.id = id;}public int getPrice() {return price;}public void setPrice(int price) {this.price = price;}public VotePrice(int id, int price) {this.id = id;this.price = price;}@Overridepublic int compareTo(VotePrice o) {if (this.getId() != o.getId()) return this.getId() - o.getId();return this.price - o.price;}
}
推荐
如果你对本系列的其他题目感兴趣,可以参考华为OD机试真题及题解(JAVA),查看当前专栏更新的所有题目。