Do not mess it up
阿新 • • 發佈:2019-01-09
牛牛和 15 個朋友來玩打土豪分田地的遊戲,牛牛決定讓你來分田地,地主的田地可以看成是一個矩形,每個位置有一個價值。分割田地的方法是橫豎各切三刀,分成 16 份,作為領導幹部,牛牛總是會選擇其中總價值最小的一份田地,作為牛牛最好的朋友,你希望牛牛取得的田地的價值和儘可能大,你知道這個值最大可以是多少嗎?
輸入描述:
每個輸入包含 1 個測試用例。每個測試用例的第一行包含兩個整數 n 和 m(1 <= n, m <= 75),表示田地的大小,接下來的 n 行,每行包含 m 個 0-9 之間的數字,表示每塊位置的價值。
輸出描述:
輸出一行表示牛牛所能取得的最大的價值。
輸入例子:
4 4 3332 3233 3332 2323
輸出例子:
2
思路:min-max問題,要求滿足一定條件求極值:Binary Search
逆向思維,不迴圈遍歷切的位置,而迴圈可以獲得的最大的數值
import java.util.*; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); while(sc.hasNext()) { int n = sc.nextInt(), m = sc.nextInt(); int[][] a = new int[n+1][m+1]; for(int i=0; i<n; i++) { String s = sc.next(); char[] cs = s.toCharArray(); for(int j=0; j<m; j++) a[i+1][j+1]=cs[j]-'0'; } // pre-process int[][] sum = new int[n+1][m+1]; for(int i=1; i<=n; i++) for(int j=1; j<=m; j++) sum[i][j] = sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j]; // 迴圈遍歷所有的情況會超時 // 這是應該換一種逆向思維,正常是遍歷然後找目標值,現在是給定一個目標值,看能不能實現 // 能實現就把目標值提高,否則就降低,自然而然就想到了BS int lo = 0, hi = sum[n][m], max = 0; while(lo <= hi) { int target = lo + (hi - lo)/2; if(canGetTarget(sum, target)) { lo = target+1; max = target; } else { hi = target-1; } } System.out.println(max); } } private static boolean canGetTarget(int[][] sum, int target) { int n =sum.length-1, m = sum[0].length; for(int i=1; i<sum.length; i++) for(int j=i+1; j<sum.length; j++) for(int k=j+1; k<sum.length; k++) { int preCutPos = 0, cnt = 0; for(int l=1; l<=n; l++) { int sum1 = sum[i][l] - sum[i][preCutPos]; int sum2 = sum[j][l] - sum[j][preCutPos] - sum[i][l] + sum[i][preCutPos]; int sum3 = sum[k][l] - sum[k][preCutPos] - sum[j][l] + sum[j][preCutPos]; int sum4 = sum[n][l] - sum[n][preCutPos] - sum[k][l] + sum[k][preCutPos]; if(sum1>=target && sum2>=target && sum3>=target && sum4>=target) { cnt++; preCutPos = l; } } if(cnt >= 4) return true; } return false; } }
有 n 個學生站成一排,每個學生有一個能力值,牛牛想從這 n 個學生中按照順序選取 k 名學生,要求相鄰兩個學生的位置編號的差不超過 d,使得這 k 個學生的能力值的乘積最大,你能返回最大的乘積嗎?
思路:剛開始用DFS,TLE,其實一個更簡單的辦法是用DP
public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); long[]a=new long[n+1]; for(int i=1;i<=n;i++)a[i]=sc.nextInt(); int k=sc.nextInt(), d=sc.nextInt(); // dp[i] means end with i long[][] dpMax = new long[n+1][k+1]; long[][] dpMin = new long[n+1][k+1]; long rst = Long.MIN_VALUE; for(int i=1; i<=n; i++) { dpMax[i][1] = a[i]; dpMin[i][1] = a[i]; rst = Math.max(rst, dpMax[i][1]); } for(int i=2; i<=n; i++) { for(int j=2; j<=k && j<=i; j++) { for(int p=i-1; i-p<=d && p>=j-1; p--) { dpMax[i][j] = Math.max(dpMax[p][j-1]*a[i], dpMin[p][j-1]*a[i]); dpMin[i][j] = Math.min(dpMax[p][j-1]*a[i], dpMin[p][j-1]*a[i]); if(j == k) rst = Math.max(rst, dpMax[i][j]); } } } System.out.println(rst); } }
牛牛的作業薄上有一個長度為 n 的排列 A,這個排列包含了從1到n的n個數,但是因為一些原因,其中有一些位置(不超過 10 個)看不清了,但是牛牛記得這個數列順序對的數量是 k,順序對是指滿足 i < j 且 A[i] < A[j] 的對數,請幫助牛牛計算出,符合這個要求的合法排列的數目。
思路:寫程式碼的時候會發現:其實有很多重複的計算,一個更好的思路是先把這些重複計算的先計算出來,用到的時候直接用,以此來節省計算量
package l25;
import java.util.*;
public class Main {
static Map<Integer, Map<Integer, Integer>> map = new HashMap<Integer, Map<Integer,Integer>>();
static List<Integer> travel = new ArrayList<Integer>();
static List<Integer>left = new ArrayList<Integer>();
static boolean[] marked;
static int rst=0;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n=sc.nextInt(), k=sc.nextInt();
int[]a=new int[n];
List<Integer>idxs = new ArrayList<Integer>();
for(int i=1; i<=n; i++) left.add(i);
for(int i=0;i<n;i++) {
a[i]=sc.nextInt();
if(a[i]==0) idxs.add(i);
else left.remove((Integer)a[i]);
}
marked=new boolean[left.size()];
// 1
int sum1=0;
for(int i=0; i<n; i++) {
if(a[i]==0) continue;
for(int j=i+1; j<n; j++) {
if(a[j]==0) continue;
if(a[j]>a[i]) sum1++;
}
}
// 2
for(int i=0; i<idxs.size(); i++) map.put(i, new HashMap<Integer, Integer>());
for(int i=0; i<idxs.size(); i++) {
Map<Integer, Integer> t = map.get(i);
for(int fill : left) {
int cnt=0;
for(int j=0; j<idxs.get(i); j++)
if(fill>a[j] && a[j]!=0) cnt++;
for(int j=idxs.get(i)+1; j<n; j++)
if(fill<a[j] && a[j]!=0) cnt++;
t.put(fill, cnt);
}
}
// 3
dfs(k-sum1, left.size());
System.out.println(rst);
}
private static void dfs(int i, int n) {
if(n == 0) {
// System.out.println(travel);
int cnt=0;
for(int j=0; j<travel.size(); j++) cnt+=map.get(j).get(travel.get(j));
for(int j=0; j<travel.size(); j++)
for(int k=j+1; k<travel.size(); k++)
if(travel.get(k)>travel.get(j)) cnt++;
if(cnt == i) rst++;
}
for(int j=0; j<left.size(); j++) {
if(!marked[j]) {
travel.add(left.get(j));
marked[j]=true;
dfs(i, n-1);
travel.remove(travel.get(travel.size()-1));
marked[j]=false;
}
}
}
}
長度為n的陣列亂序存放著0至n-1. 現在只能進行0與其他數的交換,完成以下函式
思路:也很好理解,就是0先佔據第(0,1,2,3..i..)的位置,然後再跟i交換就好了
package mock1;
public class Solution {
/**
* 交換數組裡n和0的位置
*
* @param array
* 陣列
* @param len
* 陣列長度
* @param n
* 和0交換的數
*/
// 不要修改以下函式內容
public void swapWithZero(int[] array, int len, int n) {
Main.SwapWithZero(array, len, n);
}
// 不要修改以上函式內容
/**
* 通過呼叫swapWithZero方法來排
*
* @param array
* 儲存有[0,n)的陣列
* @param len
* 陣列長度
*/
public void sort(int[] array, int len) {
// 完成這個函式
for(int i = len-1;i>=0; i--){
swapWithZero(array,len,array[array[i]]);
swapWithZero(array,len,array[i]);
}
}
}