题意:
黑板上写了n(n<=50)个不超过1000的数,
双方轮流进行以下操作中的一种:
- 将一个数减1,当某个数为0时将其擦去;
- 将两个数擦去,并将两个数之和写上黑板;
擦去最后一个数为胜者,Alice先手,求最后胜者。
思路:
如果所有数都是大于1的数,共可执行 cnt 次操作,其中:
cnt=sum(a[i])+n−1
当 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;
}