Dynamic programming 寻找动态规划解
问题: 有一个由N块砖组成的堆栈。您和您的朋友决定使用此堆栈玩游戏。在这个游戏中,一个人可以从顶部移除1/2/3块砖块,玩家移除的砖块上的数字被添加到他的分数中。你必须以这样一种方式打球,你可以获得尽可能多的分数,同时你的朋友也会以最佳状态打球,你会采取第一步 输入格式 第一行将包含一个整数T,即测试用例的数量。每个测试用例对应两行,第一行包含一个数字N,即堆栈中元素的数量,下一行包含N个数字,即从上到下写在砖上的数字 输出格式 对于每个测试用例,打印一行包含您的最高分数。 我尝试过递归,但没有成功Dynamic programming 寻找动态规划解,dynamic-programming,Dynamic Programming,问题: 有一个由N块砖组成的堆栈。您和您的朋友决定使用此堆栈玩游戏。在这个游戏中,一个人可以从顶部移除1/2/3块砖块,玩家移除的砖块上的数字被添加到他的分数中。你必须以这样一种方式打球,你可以获得尽可能多的分数,同时你的朋友也会以最佳状态打球,你会采取第一步 输入格式 第一行将包含一个整数T,即测试用例的数量。每个测试用例对应两行,第一行包含一个数字N,即堆栈中元素的数量,下一行包含N个数字,即从上到下写在砖上的数字 输出格式 对于每个测试用例,打印一行包含您的最高分数。 我尝试过递归,但没有
int recurse(int length, int sequence[5], int i) {
if(length - i < 3) {
int sum = 0;
for(i; i < length; i++) sum += sequence[i];
return sum;
} else {
int sum1 = 0;
int sum2 = 0;
int sum3 = 0;
sum1 += recurse(length, sequence, i+1);
sum2 += recurse(length, sequence, i+2);
sum3 += recurse(length, sequence, i+3);
return max(max(sum1,sum2),sum3);
}
}
int main() {
int sequence[] = {0, 0, 9, 1, 999};
int length = 5;
cout << recurse(length, sequence, 0);
return 0;
}
int递归(int长度,int序列[5],int i){
if(长度-i<3){
整数和=0;
对于(i;i cout乍一看,您的代码似乎完全错误,原因如下:
玩家不被考虑。你拿一块砖头或者你的朋友拿一块砖头是不一样的(你必须最大化你的分数,当然总分数总是砖头上分数的总和)
看起来只是某种形式的递归,没有记忆,这种方法显然会爆炸到指数计算时间(你使用的是“蛮力”方法,列举所有可能的游戏)
动态规划方法显然是可行的,因为游戏的最佳延续并不取决于你是如何达到某个特定状态的
- 下一个玩的是谁(你还是你的朋友)
- 这堆砖还剩多少块
通过这两个输入,你可以计算从那一点到游戏结束你能收集到多少
1.轮到你了
你需要尝试收集1、2或3,并在对手必须选择的下一个游戏状态递归调用。在这三种情况中,你保留最高的结果
2.轮到对手了
你需要模拟收集1块、2块或3块砖块,并在你必须选择的下一个游戏状态下递归调用。在这三种情况中,你保留了最低的结果(因为对手试图最大化他/她的结果,而不是你的结果)
在函数的一开始,你只需要检查之前是否处理过相同的游戏状态,当从计算返回时,你需要存储结果。由于这种查找/记忆,搜索时间将不是指数型的,而是不同游戏状态数的线性(仅2*N,其中N是砖块数)
在Python中:
memory = {}
bricks = [0, 0, 9, 1, 999]
def maxResult(my_turn, index):
key = (my_turn, index)
if key in memory:
return memory[key]
if index == len(bricks):
result = 0
elif my_turn:
result = None
s = 0
for i in range(index, min(index+3, len(bricks))):
s += bricks[i]
x = s + maxResult(False, i+1)
if result is None or x > result:
result = x
else:
result = None
for i in range(index, min(index+3, len(bricks))):
x = maxResult(True, i+1)
if result is None or x < result:
result = x
memory[key] = result
return result
print maxResult(True, 0)
memory={}
砖块=[0,0,9,1999]
def maxResult(我的车,索引):
键=(我的旋转,索引)
如果输入内存:
返回存储器[键]
如果索引==len(砖):
结果=0
elif my_turn:
结果=无
s=0
对于范围内的i(索引,最小值(索引+3,长度(砖)):
s+=砖[i]
x=s+maxResult(假,i+1)
如果结果为无或x>结果:
结果=x
其他:
结果=无
对于范围内的i(索引,最小值(索引+3,长度(砖)):
x=maxResult(真,i+1)
如果结果为无或x<结果:
结果=x
内存[键]=结果
返回结果
打印maxResult(真,0)
乍一看,您的代码似乎完全错误,原因如下:
玩家不被考虑。你拿一块砖头或者你的朋友拿一块砖头是不一样的(你必须最大化你的分数,当然总分数总是砖头上分数的总和)
看起来只是某种形式的递归,没有记忆,这种方法显然会爆炸到指数计算时间(你使用的是“蛮力”方法,列举所有可能的游戏)
动态规划方法显然是可行的,因为游戏的最佳延续并不取决于你是如何达到某个特定状态的
- 下一个玩的是谁(你还是你的朋友)
- 这堆砖还剩多少块
通过这两个输入,你可以计算从那一点到游戏结束你能收集到多少
1.轮到你了
你需要尝试收集1、2或3,并在对手必须选择的下一个游戏状态递归调用。在这三种情况中,你保留最高的结果
2.轮到对手了
你需要模拟收集1块、2块或3块砖块,并在你必须选择的下一个游戏状态下递归调用。在这三种情况中,你保留了最低的结果(因为对手试图最大化他/她的结果,而不是你的结果)
在函数的一开始,你只需要检查之前是否处理过相同的游戏状态,当从计算返回时,你需要存储结果。由于这种查找/记忆,搜索时间将不是指数型的,而是不同游戏状态数的线性(仅2*N,其中N是砖块数)
在Python中:
memory = {}
bricks = [0, 0, 9, 1, 999]
def maxResult(my_turn, index):
key = (my_turn, index)
if key in memory:
return memory[key]
if index == len(bricks):
result = 0
elif my_turn:
result = None
s = 0
for i in range(index, min(index+3, len(bricks))):
s += bricks[i]
x = s + maxResult(False, i+1)
if result is None or x > result:
result = x
else:
result = None
for i in range(index, min(index+3, len(bricks))):
x = maxResult(True, i+1)
if result is None or x < result:
result = x
memory[key] = result
return result
print maxResult(True, 0)
memory={}
砖块=[0,0,9,1999]
def maxResult(我的车,索引):
键=(我的旋转,索引)
如果输入内存:
返回存储器[键]
如果索引==len(砖):
结果=0
否则如果
import java.io.*;
import java.util.*;
import java.text.*;
import java.math.*;
import java.util.regex.*;
public class Solution {
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
int noTest=sc.nextInt();
for(int i=0; i<noTest; i++){
int noBrick=sc.nextInt();
ArrayList<Integer> arr=new ArrayList<Integer>();
for (int j=0; j<noBrick; j++){
arr.add(sc.nextInt());
}
long sum[]= new long[noBrick];
sum[noBrick-1]= arr.get(noBrick-1);
for (int j=noBrick-2; j>=0; j--){
sum[j]= sum[j+1]+ arr.get(j);
}
long[] max=new long[noBrick];
if(noBrick>=1)
max[noBrick-1]=arr.get(noBrick-1);
if(noBrick>=2)
max[noBrick-2]=(int)Math.max(arr.get(noBrick-2),max[noBrick-1]+arr.get(noBrick-2));
if(noBrick>=3)
max[noBrick-3]=(int)Math.max(arr.get(noBrick-3),max[noBrick-2]+arr.get(noBrick-3));
if(noBrick>=4){
for (int j=noBrick-4; j>=0; j--){
long opt1= arr.get(j)+sum[j+1]-max[j+1];
long opt2= arr.get(j)+arr.get(j+1)+sum[j+2]-max[j+2];
long opt3= arr.get(j)+arr.get(j+1)+arr.get(j+2)+sum[j+3]-max[j+3];
max[j]= (long)Math.max(opt1,Math.max(opt2,opt3));
}
}
long cost= max[0];
System.out.println(cost);
}
}
}
# Base Cases
dp[0] = a[0]
dp[1] = a[0]+a[1]
dp[2] = a[0]+a[1]+a[2]
# build prefix sum array
pre = [a[0]]
for i in range(1,n):
pre.append(pre[-1]+a[i])
ans1 = a[i] + (pre[i-1] - dp[i-1])
ans1 = a[i]+ (pre[i-1] - dp[i-1]) # if we pick only ith brick
ans2 = a[i]+a[i-1]+(pre[i-2] - dp[i-2]) # pick 2 bricks
ans3 = a[i]+a[i-1]+a[i-2]+(pre[i-3] - dp[i-3]) # pick 3 bricks
a = map(int, raw_input().split())
a.reverse() # so that a[0] is bottom brick of stack
dp = [0 for x1 in xrange(n)]
dp[0] = a[0]
dp[1] = a[0]+a[1]
dp[2] = a[0]+a[1]+a[2]
# build prefix sum array
pre = [a[0]]
for i in range(1,n):
pre.append(pre[-1]+a[i])
for i in xrange(3,n):
# We can pick brick i, (i,i-1) or (i,i-1,i-2)
ans1 = a[i]+ (pre[i-1] - dp[i-1]) # if we pick only ith brick
ans2 = a[i]+a[i-1]+(pre[i-2] - dp[i-2]) # pick 2
ans3 = a[i]+a[i-1]+a[i-2]+(pre[i-3] - dp[i-3]) #pick 3
# both players maximise this value. Doesn't matter who is playing
dp[i] = max(ans1, ans2, ans3)
print dp[n-1]