mini notes

競技プログラミングの解法メモを残していきます。

DPまとめコンテスト K - Stones

K - Stones

概要

数列a_{N}を用いて、二人が次のようなゲームを行う。

  • 最初は石がK個ある
  • 自分の手番には、数列の中から1つの数を選び、その数だけ石を減らす。ただし、残りの石の個数より多いような数列の数を選んではいけない

最初に操作できなくなった方を負けとする。どちらが勝つかを判定せよ。

制約

1 \leq N \leq 100
1 \leq K\leq 10^5
1 \leq a_{i} \leq Ka_{i}は狭義単調増加

方針

dp[i] :残りの石がi個のとき、先手が勝つかどうか とする。
DPの遷移としては下記の通り。

  • 石が0個の時は先手の負け
  • 石が残りi - a[j]個の時に先手必敗とすると、石が残りi個のときに先手がa[j]を選択すると先手が勝てるため、石が残りi個の時は先手必勝

(配列の小さい方から先手必敗のケースを順に見てゆき、そのケースの1手前が先手必勝になるイメージ)

解答

Submission #3969759 - Educational DP Contest / DP まとめコンテスト

#include <bits/stdc++.h>
 
#define FOR(i,a,b) for(int i=(a);i<(b);i++)
#define rep(i,n) FOR(i,0,n)
#define RFOR(i,a,b) for(int i=(a)-1;i>=(b);i--)
#define rrep(i,n) RFOR(i,n,0)
 
using namespace std;
 
typedef long long ll;
typedef unsigned long long ull;
 
int main()
{
	cin.tie(0);
	ios::sync_with_stdio(false);
 
	int n, k;
	cin >> n >> k;
 
	int a[n];
	rep(i, n) cin >> a[i];
 
	bool dp[k+1];
	// dp[i]:石が残りi個のとき、先手が勝てるか
	rep(i, k+1) dp[i] = false;
 
	for(int i = 1; i <= k; i++){
		for(int j = 0; j < n; j++){
			if(i >= a[j]){
				dp[i] |= !dp[i-a[j]];
			}
		}
	}
 
	cout << (dp[k] ? "First" : "Second") << endl;
}