1. 程式人生 > >Do not mess it up

Do not mess it up

牛牛和 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]);
    	 }
    }
}