网易编程题(3)

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



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值