POJ 2068 Nim (博弈和DP本是一家=-=)

POJ 2068 Nim

题意:

2n个人围成一圈,编号为奇数的一队,编号为偶数的一队,轮流从原本S个石头的石堆进行拿石头操作

每个人可拿石头范围在[1,m[i]]内(m[i]表示第i个人最多能拿的石头数)

问先手(0所在的偶数队)能否取胜

分析:

dp[i][j]表示第i个人面对j个石头的局面 是否能胜(1/0)
只要有“败”的子状态就是胜态
如果子状态全为胜态则为败态
状态转移:
在dp[(i+1)%2n][j-x]中找败态(x ∈ [1,m[i]])
表示第i个人拿走x个石头,让下一个人
(i+1)%2n面对j-x个石头的局面 

记忆化搜索=-= 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<vector>
#include<algorithm>
#define mem(a,x) memset(a,x,sizeof(a))
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
/*
	dp[i][j]表示第i个人面对j个石头的局面 是否能胜
	只要有“败”的子状态就是胜态
	如果子状态全为胜态则为败态
	状态转移:
	在dp[(i+1)%2n][j-x]中找败态
	表示第i个人拿走x个石头,让第(i+1)%2n个人面对j-x个石头的局面 
	
	记忆化搜索=-= 
*/
int dp[22][1<<14]; 
int m[22];
int n,s;
int DP(int i,int j)
{
	if (j == 0) return 1;//没有石头的局面为胜态 
	if (~dp[i][j]) return dp[i][j];
	for (int x = 1;x <= m[i];++x)
	{
		if (j-x<0) break;
		if (DP((i+1)%(2*n),j-x) == 0) return dp[i][j] = 1;
	}
	return dp[i][j] = 0;
} 
int main()
{
	while (cin>>n)
	{
		if (!n) break;
		cin>>s;
		for (int i = 0;i < 2*n;++i) cin>>m[i];
		mem(dp,-1);
		cout<<DP(0,s)<<endl;
	}
	return 0;
}
java:
//package acm.poj2068;
import java.util.*;
public class Main {
	static int n , s;
	static int dp[][] = new int[22][1<<14];
	static int m[] = new int [22];
	static void init(){
		for (int i = 0;i <= 2*n;++i){
			Arrays.fill(dp[i], -1);
		}
	}
	static int DP(int i,int j){
		if (j == 0) return 1;
		if (dp[i][j]!=-1) return dp[i][j];
		for (int x = 1;x <= m[i];++x){
			if (j-x<0) break;
			if (DP((i+1)%(2*n),j-x) == 0) return dp[i][j] = 1;
		}
		return dp[i][j] = 0;
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		@SuppressWarnings("resource")
		Scanner in = new Scanner(System.in);
		while (in.hasNext()){
			n = in.nextInt();
			if (n == 0) break;
			s = in.nextInt();
			for (int i = 0;i < 2*n;++i){
				m[i] = in.nextInt();
			}
			init();
			System.out.println(DP(0,s));
		}
	}

}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值