微软2017第二场回顾总结
http://hihocoder.com/contest/mstest2017april/problems
1.皇后问题
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
Map<Integer, Integer> row = new HashMap();
Map <Integer, Integer> col = new HashMap();
Map<Integer, Integer> up = new HashMap();
Map <Integer, Integer> dow = new HashMap();
int t1 = 0;
int t2 = 0;
int t3 = 0;
int t4 = 0;
for (int i =0; i < n; i++) {
t1 = sc.nextInt();
t2 = sc.nextInt();
t3 = t1 + t2;
t4 = t1 - t2;
if (row.containsKey(t1)) {
row.put(t1, row.get(t1) + 1);
} else {
row.put(t1, 1);
}
if (col.containsKey(t2)) {
col.put(t2, col.get(t2) + 1);
} else {
col.put(t2, 1);
}
if (up.containsKey(t3)) {
up.put(t3, up.get(t3) + 1);
} else {
up.put(t3, 1);
}
if (dow.containsKey(t4)) {
dow.put(t4, dow.get(t4) + 1);
} else {
dow.put(t4, 1);
}
}
int resu = 0;
int temp = 0;
for (Integer i1 : row.keySet()) {
temp = row.get(i1);
if (temp >= 2) {
resu += temp * (temp -1) / 2;
}
}
for (Integer i1 : col.keySet()) {
temp = col.get(i1);
if (temp >= 2) {
resu += temp * (temp -1) / 2;
}
}
for (Integer i1 : up.keySet()) {
temp = up.get(i1);
if (temp >= 2) {
resu += temp * (temp -1) / 2;
}
}
for (Integer i1 : dow.keySet()) {
temp = dow.get(i1);
if (temp >= 2) {
resu += temp * (temp -1) / 2;
}
}
System.out.println(resu);
}
2机器人再生产问题
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
long n = sc.nextLong();
long q = sc.nextLong();
float robot = 1;
float gain = n;
long time = 0;
while (gain / 2 > q) {
robot = robot * 2;
gain = gain / 2;
time += 1;
}
double left = Math.ceil(n / robot);
long result = (long) (time * q + left);
System.out.println(result);
long temp = 1;// 记录再生产次数
long nowstate = n;
long robots = 2;
long bigerstate = q * temp + (long)Math.ceil(((double)n) / robots);
while (bigerstate < nowstate) {
nowstate = bigerstate;
robots = robots * 2;
temp++;
bigerstate = q * temp + (long)Math.ceil(((double)n) / robots);
}
System.out.println(nowstate);
}
3硬币均衡处理
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
long[] rowA = new long[n];
long[] rowB = new long[n];
long sum = 0;
for (int i = 0; i < n; i++) {
rowA[i] = sc.nextInt();
rowB[i] = sc.nextInt();
sum = sum + rowA[i] + rowB[i];
}
long avg = sum / 2 / n;
long move = 0;
for (int i = 0; i < n - 1; i++) {
long sumAB = rowA[i] + rowB[i];
if (rowA[i] < avg && rowB[i] > avg) {
if (sumAB < 2 * avg) {
move += rowB[i] - avg;
rowA[i] = sumAB - avg;
rowB[i] = avg;
} else {
move += avg - rowA[i];
rowA[i] = avg;
rowB[i] = sumAB - avg;
}
} else if (rowA[i] > avg && rowB[i] < avg) {
if (sumAB < 2 * avg) {
move += rowA[i] - avg;
rowA[i] = avg;
rowB[i] = sumAB - avg;
} else {
move += avg - rowB[i];
rowA[i] = sumAB - avg;
rowB[i] = avg;
}
}
if (rowA[i] < avg) {
move += avg - rowA[i];
} else {
move += rowA[i] - avg;
}
rowA[i + 1] = rowA[i + 1] + rowA[i] -avg;
if (rowB[i] < avg) {
move += avg - rowB[i];
} else {
move += rowB[i] - avg;
}
rowB[i + 1] = rowB[i + 1] + rowB[i] -avg;
System.out.println(move);
}
if (rowA[n - 1] < avg) {
move += avg - rowA[n - 1];
} else {
move += rowA[n - 1] - avg;
}
System.out.println(move);
}
4.刺杀元首任务
这个从底层的bfs 到 变相背包问题
// 本case是跑通了的
// 有点像写代码的样子了
import java.util.Arrays;
import java.util.Scanner;
public class main {
static int[] FA = new int [2005];
static int[] IN = new int[2005];
static int[] IP = new int[2005];
static int[] C = new int[2005];
static int leafnums = 0;
static int n ;
static boolean[] leafs = new boolean[2005];
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
Arrays.fill(leafs, false);
int root = 0;
for (int i = 1; i <= n; i++) {// 按照 id来的 从1 开始
FA[i] = sc.nextInt();// father id
IN[i] = sc.nextInt();// INi
IP[i] = sc.nextInt();// Ifo Paren
C[i] = sc.nextInt();//Cost i
if (FA[i] == 0) {
root = i;// id
}
if (IN[i] == 0) {
leafs[i] = true;
leafnums++;
}
}
// bfs + dp(背包问题)
while (leafnums > 1) {
int temp = findNextId(1);//找到最底层可解的叶子节点
if(temp < 0) break;
int faid = FA[temp];
changeFather(faid);
}
System.out.println(C[root]);
}
public static int findNextId(int index) {// find first leaf whoeos father chirldren ia la leafs
int temp = index;
while (!leafs[temp] && temp < leafs.length) {
temp++;
}
if (temp == leafs.length) {
return -1;
}
int faid = FA[temp];
for (int i = 1; i <= n; i++) {// test all the chirld of the fa is cul
if (FA[i] == faid ) {
if (leafs[i] == true) {}
else {
return findNextId(temp + 1);
}
}
}
return temp;
}
public static void changeFather(int faid) {
int[] chirldren = new int[2005];
int chiindex= 0;
for (int i = 1; i <= n; i++) {// fid id
if (FA[i] == faid ) {
chirldren[chiindex++] = i;// chrldren id
leafs[i] = false;
leafnums--;
}
}
int[] infos = new int[chiindex + 1];
int[] costs = new int[chiindex + 1];
for (int i = 1; i <= chiindex; i++) {
int id = chirldren[i - 1];
infos[i] = IP[id];
costs[i] = C[id];
}
int info = IN[faid];// 包
int[][] dp = new int[chiindex + 1][info + 1];
for (int i = 0; i <= info; i++) {
dp[0][i] = Integer.MAX_VALUE;
}
for (int i = 0; i <= chiindex; i++) {
dp[i][0] = costs[i];
}
for (int i = 1; i <= chiindex; i++) {
for (int j = 1; j <= info; j++) {
if (j <= infos[i]) {
dp[i][j] = Math.min(dp[i - 1][j], costs[i]);
} else {
if (dp[i - 1][j - infos[i]] == Integer.MAX_VALUE) {
dp[i][j] = dp[i - 1][j];
} else {
dp[i][j] = Math.min(dp[i - 1][j], dp[i - 1][j - infos[i]] + costs[i]);
}
}
}
}
C[faid] = C[faid] + dp[chiindex][info];
leafs[faid] = true;
leafnums++;
}
}
//针对这个背包问题,还是要好好总结dp的思路,一般简单画画会出成果
走一波贪心问题
所谓贪心算法指的是为了解决在不回溯的前提之下,找出整体最优或者接近最优解的这样一种类型的问题而设计出来的算法。贪心算法的基本思想是找出整体当中每个小的局部的最优解,并且将所有的这些局部最优解合起来形成整体上的一个最优解。因此能够使用贪心算法的问题必须满足下面的两个性质:1.整体的最优解可以通过局部的最优解来求出;2.一个整体能够被分为多个局部,并且这些局部都能够求出最优解。
判断问题是叹息你问题还是dp问题,一个策略是当前的结果是否依赖于前面的前n结果,背包问题就依赖于前面很多个的结果。
435. Non-overlapping Intervals
最小的,可以考虑直接删除,为什么呢,因为是按照end进行的升序,然后如果start 小于end了,那么start肯定是有问题的,也有可能问题最多,今天解决贪心问题跟CC150
44. Wildcard Matching通配符匹配问题
这个虽然归结到贪心问题里面,但是用dp会非常好理解,故采用dp算法。
错误原因:初始化时出现了错误
代表任意子串,正则表达式则不一样,代表的是0个或者任意多个前一个字符。
dp[i + 1][j + 1] = dp[i + 1][j] || dp[i][j + 1];详尽的解释在下面
This idea is:
if(dp[i][j + 1] == true) –> s(0–>i-1) matches with p(0–>j) now you see p[j] == ‘’ , in the end of s, you can add another or more k chars due to the last of pattern is ‘‘, making dp[i + 1][j + 1] or even dp[i+k][j + 1] true;
if(dp[i + 1][j] == true) –> s(0–>i) matches with p(0–>j-1) now you see p[j] == ‘‘, add this into p, can match none in s. you don’t need to add anything into s, you can see now dp[i + 1][j + 1] = true.
还有一种思路是直接基于字符串匹配的
10. Regular Expression Matching正则表达式问题
分析思路,当 pattern.charAt(j) == ‘*’的时候,判断 j - 1的位置是否和 i匹配或者
if (p.charAt(j) == '*') {
if (p.charAt(j - 1) != s.charAt(i) && p.charAt(j - 1) != '.'){
dp[i + 1][j + 1] = dp[i + 1][j - 1];//这里面的else的条件是 p.charAt(j - 1) == s.charAt(i) || p.charAt(j - 1) == '.' 也就是说如果前面匹配,就有的选是否是取前一个字符复制,否则就没得选,只能取0,看 str 的0 - i和 patten的0 到 j - 2是否匹配
} else {
dp[i + 1][j + 1] = dp[i + 1][j - 1] || dp[i][j + 1];// 如果 从两个字符串匹配角度分析
}
}
55Jump Game
很简单也很常规的贪心问题,从后向前
45. Jump Game II
三个缓存,常数空间,每当i到达temp结点的位置,就更新temp的值为当前curFast的最远值
134. Gas Station
加气站这个之前没ac是忽略掉了每一个gas[i] > cost[i] 都可以作为起点,
trick: 如果AD不可达,那么BD,CD同样不可达,可以直接从D开始循序
135. Candy
分糖果问题,简单的两次循环即可。从前向后,然后从后向前。
316. Remove Duplicate Letters
要求:删除重复元素,并且删除后形成的数组保证是字典顺序最小的。
思路: 1先选出第一位置应该放的元素,然后递归的调用函数,进行求解,
策略: 每次只选取第一个pos,然后再剩余的里面除去pos所处的字符,然后进行递归的调用。
2.利用stack进行缓存,策略也是一样的,如果当前的字母比栈顶元素的字母排序靠前,并且栈顶元素在后面还会出现,进行出栈操作。这样维持的栈就是从目前为止,最优解,不存在重复计算的地方。
321. Create Maximum Number
要求:给定两个数组,一个数,在保证原数组相对顺序下,得到最大的长度为k的数组。
public int[] maxNumber(int[] nums1, int[] nums2, int k) {
int[] resu = new int[k];
int len1 = nums1.length;
int len2 = nums2.length;
for (int i = Math.max(0, k - len2); i <= k && i <= len1; i++) {
int[] temp = merge(maxArray(nums1, i), maxArray(nums2, k - i), k);
if (greater(temp, 0, resu, 0)) resu = temp;
}
return resu;
}
public int[] merge (int[] nums1, int[] nums2, int k) {
int[] ans = new int[k];
for (int i = 0, j = 0, r = 0; r < k; r++) {
ans[r] = greater(nums1, i, nums2, j) ? nums1[i++] : nums2[j++];
}
return ans;
}
public boolean greater (int[] nums1, int i, int[] nums2, int j) {
while (i < nums1.length && j < nums2.length && nums1[i] == nums2[j]) {
i++;
j++;
}
return j == nums2.length || ( i < nums1.length && nums1[i] > nums2[j]);
}
public int[] maxArray(int[] nums, int k) {
int [] ant = new int[k];
int n = nums.length;
for (int i = 0, j = 0; i < n; i++) {
while (n - i + j > k && j > 0 && ant[j - 1] < nums[i] ) j--;// n - i 表示我的nums里面还剩余多少, + j 表示 后面还能组成多少位, 大于 k 表示我这边还能更新,
if (j < k) ant[j++] = nums[i];
}
return ant;
}
330. Patching Array
无思路:给定一个数组,给定一个数n,要求数组中的最少添加几个数可以得到从1到n的数
思路:如果数组中存在能组成的数字为[1, n), 那么可以组成[1, n(n + 1) / 2),则直接判断 n(n + 1) / 2。
376. Wiggle Subsequence
要求:给定一个数组,可以删除部分数据,保证数据的相对顺序
思路:无思路
ac: 1,采用dp,构建两个数组,up 以及down
2.采用贪心,也是感觉也是一样的方法
public int wiggleMaxLength(int[] nums) {
if (nums.length == 0) return 0;
int n = nums.length, d = 1, u = 1;
for (int i = 1; i < nums.length; i++) {
if (nums[i] > nums[i - 1]) u = d + 1;
else if (nums[i] < nums[i - 1]) d = u + 1;
}
return Math.max(u, d);
}
392. Is Subsequence
给定两个字符串s, t, 判断s是不是t的子串,中间可以存在间隔
思路:两种方法,一个是遍历t,
另外一个是遍历s,然后调用indexOf函数,
406. Queue Reconstruction by Height
给定people数组,第一个表示身高,第一个表示身高大于等于自己的前面的人数,输出符合规则的结果
思路:无思路
ac:身高降序,前面的人数升序,用一个list进行插入。最后转成数组。
435. Non-overlapping Intervals
要求:最多的不重复的间隔,运用思想为 least is most,很多时候都是需要从反面进行考虑的
思路:按照终点进行升序排列,求出不重叠的个数,然后用全部的减去不重叠的个数,就是重叠区域覆盖的,反面考虑,多少个不重叠的,得到的将是重叠的
452. Minimum Number of Arrows to Burst Balloons
要求:要把所有气球都都用箭穿透,至少需要多少支箭
思路:先按照起点以及终点进行排序,然后记录end,如果覆盖了,就选取更新end,如果不能覆盖,则新增箭
455. Assign Cookies
分曲奇,有n个孩子的饥饿系数数组g,同时也存在曲奇的c分数,问最大满足的数量,直接进行排序
502. IPO
上市,做任务,给定初始资金w,给定任务数k,给定利润数组,给定成本数组,选取k个任务,使得最后得到最大的收益
思路:无思路
ac:采用两个队列进行缓存
630. Course Schedule III
要求:课程,持续时间,deadline
思路:先针对结束时间进行升序排序,然后对持续时间进行加入队列的处理,如果超时,就删除消耗时间最长的课程
for (int i = 0; i < courses.length;i++) {
time += courses[i][0];
pro.add(courses[i][0]);
if (time > courses[i][1]) time -= pro.poll();
}
return pro.size();
210. Course Schedule II
要求:课程之间相互依赖
思路:先完成无依赖的课程
659. Split Array into Consecutive Subsequences
要求:扑克牌三连续顺子
思路:无思路,这里面用到了两个hashmap,core code
if (freq.get(i) == 0) continue;
else if (appendfreq.getOrDefault(i,0) > 0) {
appendfreq.put(i, appendfreq.get(i) - 1);
appendfreq.put(i+1, appendfreq.getOrDefault(i+1,0) + 1);
}
else if (freq.getOrDefault(i+1,0) > 0 && freq.getOrDefault(i+2,0) > 0) {
freq.put(i+1, freq.get(i+1) - 1);
freq.put(i+2, freq.get(i+2) - 1);
appendfreq.put(i+3, appendfreq.getOrDefault(i+3,0) + 1);
}
else return false;
freq.put(i, freq.get(i) - 1);
design
341 Flatten Nested List Iterator
思路:递归
380. Insert Delete GetRandom O(1)
思路:list + map
381. Insert Delete GetRandom O(1) - Duplicates allowed
ArrayList<Integer> nums;
HashMap<Integer, Set<Integer>> locs;
java.util.Random rand = new java.util.Random();
173. Binary Search Tree Iterator
思路:利用stack,每次出栈,都将右子节点的全部左子节点入栈
208. Implement Trie (Prefix Tree)
单词查找树
211. Add and Search Word - Data structure design
单词查找树,如果是点,就全部找不是null的地方
297. Serialize and Deserialize Binary Tree
思路:前序遍历,递归求解,用逗号分隔,组成字符串,析构,反向,注意转化成list
Deque<String> nodes = new LinkedList<String>();
nodes.addAll(Arrays.asList(data.split(spliter)));
return getTree(nodes);
295. Find Median from Data Stream
思路:大顶堆与小顶堆