Google-HK-2012-interview-"pizza"(1)

本文讲述了作者在万圣节时遇到的一个编程挑战:如何在两个朋友分享的情况下最大化自己吃到的pizza总和。问题描述为一个环形结构的pizza,通过动态规划算法来寻找最佳解决方案。虽然最初没有想出答案,但在朋友的启发下进行了尝试,并通过编程实现。然而,由于缺乏测试用例,代码可能存在错误,后续会进行修正。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今晚万圣节,和汉堡“孤男寡男”吃完泰餐,从Downtown回来,看到I-80高速都堵了。果然是落单的人最怕过节,尤其是秋雨过后,冷冷的空气平添几分对单身狗的敌意。
汉堡吐了吐烟圈说:提起女神就闹心,女神都不理我。
我说为什么不换个别的。他说,喜欢,没办法。
他问我,你就没有喜欢得非她不可的么。
我说没有。也许曾经有。

早上和Parker大神讨论了一个新的idea,大概是想要做类似OJ的网站,Parker说太难,而且test case的validate工作量十分大,建议我找到工作之后有空去写这样的web。然后他提到他在面HK Google的一道题目,叫我解。我没有想出来,最后照着他得思路我做了一下。


=====================================================

#1 题目是这样的:(好吧,描述是我自己脑补的,毕竟万圣节一个人写代码,呵呵)

万圣节,你一个人在寝室写代码,突然饿了,冰箱没有吃的东西了,于是你订了一份pizza。开门的时候看到送外卖的小哥cosplay成Fin the Human(好吧,我承认我喜欢Adventure time),他看到我一脸寂寞就知道是程序员,说,小费少收一点。好吧,你赢了。

我准备吃pizza了,这时候两个寂寞的损友也过来我家了,说要蹭饭。好,现在问题来了:

pizza被切成了3 * n片,每一片pizza有大有小,假设大小用pizza[i]表示。每次我选择一片pizza[i]吃,两个损友就会吃pizza[i-1]和pizza[i+1],注意这是环,自动脑补。

那么我怎么可以保证我所吃到的pizza总和最大呢?


=====================================================

于是我想写个程序来解决这个问题。

孤独的我继续写代码,听着好久没有听过的豆瓣FM,不知道为什么douban的随机开始又符合自己口味了,开始尝试新花样了。估计machine learning又用了新的算法,或者库更加完善了。

"why did the blues go on singing."

原来邓紫棋唱的《后会无期》的主题曲是翻唱1997年的Julie London唱的<The end of the world>。

扯远了,实在太悲伤。赶紧回来。

=====================================================

#2 算法:

考虑这个ring,Greedy算法行不通,比方说{1, 1, 1, 9, 10, 9}。
考虑使用dp,当时Parker说使用一个3n-bit的flag去表明吃pizza的状态,那么这个dp的复杂度是O(2^n)。所以要选择更好的dp的状态。

好,我们换个思路。假设我们已知:dp[i][j]表示从第i块pizza到第j块pizza之间,自己能得到的最大值。注意,这里pizza(i)和pizza(j)不允许连成一个ring,只能是line。
那么之前的pizza问题就可以转换为线性问题:假设最后取的3块pizza分别是x, y, z。那么这3块pizza所分割成为的line就是之前dp[i][j]所表示的问题。

自己可以吃的pizza的最大值就是(假设x < y < z):
max{x, y, z} + dp[x+1][y-1] + dp[y+1][z-1] + dp[z+1][x-1+n]
最后这里从z+1回到x-1有个突变点,在处理数据的时候只要copy一份pizza的数据到原始的pizza的末端就可以了
只要枚举所有的x,y,z组合情况(O(n^3)复杂度),计算好dp,然后取n^3情况中的最大值就可以了。

于是问题变成如何求解dp[i][j]。其中必须满足(j - i + 1) % 3 == 0。否则无法取到最后3块pizza(每次吃的时候都是连续3块pizza被吃掉的)。

计算dp[i][j]的时候,有两种情况:
#1 pizza[i]和pizza[j]是在最后一次被其他两个朋友同时吃掉的。只需要枚举自己吃的最后一块k。
dp[i][j] = k + dp[i+1][k-1] + dp[k+1][j-1]。要求[(k-1)-(i+1)+1]%3==0,剩下另外一边当然也是3得倍数(因为i,j有限制)。
#2 pizza[i]和pizza[j]分别在两次被其他两个朋友吃掉的。那么枚举分割点k。
dp[i][j] = dp[i][k] + dp[k+1][j]。要求(k-i+1)%3==0


=====================================================

#3 数据结构:

dp[i][j]就足够了。

=====================================================

#4 代码:

大神们请轻拍,因为实在苦于没有test case,只能瞎猜了。。。

这里的代码是有误的,请看(2),我自己写了一些test case并且修改了一下代码。

public class Solution {
	// solve the problem
	public static int maxPizzaValue(int[] pizza) {
		int n = pizza.length;
		int maxPizza = 0;

		// copy pizza to pizza
		int[] bigPizza = new int[2*n];
		System.arraycopy(pizza, 0, bigPizza, 0, n);
		System.arraycopy(pizza, 0, bigPizza, n, n);
		int[][] dp = calDP(n, bigPizza);

		for (int x = 0; x < n-2; x++) {
			for (int y = x+1; y < n-1; y++) {
				for (int z = y+1; z < n; z++) {
					// check the valid division
					if ((y - x - 1) % 3 == 0 && (z - y - 1) % 3 == 0) {
						int val = 
						max(x, y, z) + dp[x+1][y-1] + dp[y+1][z-1] + dp[z+1][x-1+n];
						if (val > maxPizza) maxPizza = val;
					}
				}
			}
		}
		return maxPizza;
	}

	// calculate dp elements
	// dp[i][j] means max value from index i to j
	public static int[][] calDP (int n, int[] A) {
		int[][] dp = new int[2*n][2*n]; 
		// init
		for (int i = 0; i < 2*n; i++) {
			for (int j = 0; j < 2*n; j++) {
				dp[i][j] = 0;
			}
		}
		// base case
		for (int i = 0; i <= 2*n-3; i++) {
			dp[i][i+2] = max(A[i], A[i+1], A[i+2]); // base case
		}
		int len = 6;
		while (len <= n) {
			for (int i = 0; i <= 2*n-len; i++) {
				int j = i+len-1;
				// must take the i, j at the same round
				for (int k = i+1; k < j; k += 3) {
					int val = A[k];
					val += k-1 < i+1 ? 0 : dp[i+1][k-1];
					val += j-1 < k+1 ? 0 : dp[k+1][j-1];
					dp[i][j] = dp[i][j] > val ? dp[i][j] : val;
				}
				// take the i, j at different round
				for (int k = i; k <= j; k++) {
					if ((k - i + 1) % 3 == 0) {
						int val = dp[i][k] + dp[k+1][j];
						dp[i][j] = dp[i][j] > val ? dp[i][j] : val;
					}
				}
			}
			len += 3;
		}
		return dp;
	}

	// return the max of the 3
	public static int max(int x, int y, int z) {
		int max = x > y ? x : y;
		max = max > z ? max : z;
		return max;
	}
}











评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值