題意:
黑板上寫了n(n<=50)個不超過1000的數,
雙方輪流進行以下操作中的一種:
- 將一個數減1,當某個數爲0時將其擦去;
- 將兩個數擦去,並將兩個數之和寫上黑板;
擦去最後一個數爲勝者,Alice先手,求最後勝者。
思路:
如果所有數都是大於1的數,共可執行 cnt 次操作,其中:
當 cnt 爲奇數時先手勝利,偶數時後手勝利。
如果有的數爲1,把1與其他數合併,操作數-1;把1直接-1,操作數-2.於是我們可以根據這個轉移規則來進行dp。
dp[i][j] = 1的個數爲i,其他>1的數可操作次數爲j時的勝負情況。
當然了,找規律也可以。
代碼:
//記憶化dp
#include<bits/stdc++.h>
using namespace std;
int n ;
const string win[2] = { "Bob", "Alice" } ;
int dp[55][50005] ;
bool dfs( int n , int m )
{
if ( dp[n][m] != -1 ) return dp[n][m] ;
int &res = dp[n][m] = 0 ;
if ( !n ) return res = ( m & 1 );
if ( !m ) return res = ( n % 3 != 0 ) ;
if ( n == 1 ) return res = 1 ;
if ( m == 1 ) return res = dfs( n + 1 , 0 ) ;
if ( !dfs( n - 1 , m ) ) res = 1 ;
if ( m > 1 && !dfs( n - 1 , m + 1 ) ) res = 1 ;
if ( m > 1 && !dfs( n , m - 1 ) ) res = 1 ;
if ( n > 1 && !dfs( n - 2 , m? m + 3 : m + 2 ) ) res = 1 ;
return res ;
}
int main()
{
int t ; cin >> t ; int kase = 1 ;
memset( dp , -1 , sizeof dp ) ;
while ( t-- ) {
scanf( "%d" , &n ) ;
int one = 0 , more = 0 ;
for ( int i = 0 ; i < n ; i++ ) {
int tmp ; scanf( "%d" ,&tmp ) ;
if ( tmp == 1 ) one++ ;
else more += tmp ;
}
if ( one != n ) more += n - one - 1 ;
bool ok = dfs( one , more ) ;
printf( "Case #%d: " , kase++ ) ;
cout << win[ok] << endl ;
}
return 0;
}
//找規律
#include<bits/stdc++.h>
using namespace std;
const string win[2] = { "Bob", "Alice" } ;
int n ;
bool check( int cnt , int sum )
{
if ( sum == cnt || sum == cnt + 2 ) {
if ( cnt % 3 == 0 ) return 0 ;
else ;
} else {
sum += ( n - 1 ) ;
if ( sum % 2 == 0 && cnt % 2 == 0 ) return 0 ;
}
return 1 ;
}
int main()
{
int t ; cin >> t ; int kase = 1 ;
while ( t-- ) {
scanf( "%d" , &n ) ;
int one = 0 , sum = 0 ;
for ( int i = 0 ; i < n ; i++ ) {
int tmp ; scanf( "%d" ,&tmp ) ;
if ( tmp == 1 ) one++ ;
sum += tmp ;
}
bool ok = check( one , sum ) ;
printf( "Case #%d: " , kase++ ) ;
cout << win[ok] << endl ;
}
return 0;
}