牛牛和 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交换就好了
http://www.cnblogs.com/AndyJee/p/4583600.html
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]);
}
}
}