1. 程式人生 > >●CodeChef Sereja and Game

●CodeChef Sereja and Game

pan ont ems while mes 實現 efi 數組 random

題鏈:

https://www.codechef.com/problems/SEAGM
題解:

概率dp,博弈論
詳細題解:http://www.cnblogs.com/candy99/p/6504340.html
本體的先手勝與不勝(第一問)以及勝的概率(第二問)都是通過dp完成的,記憶化搜索實現。

定義了一個非常妙的dp狀態:(第一問)
dp[g][c]表示當前選的數的gcd為g,且選了c個數時當前操作的人勝還是不勝。
有了這個狀態,配合預處理的cnt[g]數組(表示有cnt[g]個數為g的倍數),
就可以很巧妙的實現dp轉移:
0.if(g==1) dp[g][c]=1
1.if(c<cnt[g]&&!dp[g][c+1]) dp[g][c]=1


2.if(gcd(A[i],g)!=g&&!dp[gcd(A[i],g)][c+1]) dp[g][c]=1
第二問與第一問的方法相同,只是換成了隨機情況下求概率而已。
(如果看不太懂各種解釋,建議直接服用代碼。2333)


代碼:

#include<bits/stdc++.h>
#define MAXN 105
using namespace std;
int N,Case;
int A[MAXN],cnt[MAXN];
int opt[MAXN][MAXN];
double rad[MAXN][MAXN];
int gcd(int a,int b){
	while(b^=a^=b^=a%=b);
	return a;
}
int dfs_optimaly(int g,int c){
	int &ret=opt[g][c];
	if(g==1) ret=1;
	if(ret!=-1) return ret;
	ret=0;
	if(c<cnt[g]&&!dfs_optimaly(g,c+1)) ret=1;
	for(int i=1;i<=N;i++){
		int gg=gcd(g,A[i]);
		if(gg==g) continue;
		if(!dfs_optimaly(gg,c+1)) ret=1;
	}
	return ret;
}
double dfs_randomly(int g,int c){
	double &ret=rad[g][c];
	if(g==1) ret=1;
	if(ret>-0.5) return ret;
	ret=0;
	if(c<cnt[g]) ret+=1.0*(cnt[g]-c)/(N-c)*(1-dfs_randomly(g,c+1));
	for(int i=1;i<=N;i++){
		int gg=gcd(g,A[i]);
		if(gg==g) continue;
		ret+=1.0/(N-c)*(1-dfs_randomly(gg,c+1));
	}
	return ret;
}
int main(){
	for(scanf("%d",&Case);Case;Case--){
		scanf("%d",&N); int g=0;
		for(int i=1;i<=N;i++) scanf("%d",&A[i]),g=gcd(g,A[i]);
		if(g!=1){printf("%d %.4lf\n",N&1,1.0*(N&1)); continue;}
		for(int i=0;i<=100;i++){
			cnt[i]=0;
			for(int j=0;j<=N;j++)
				opt[i][j]=-1,rad[i][j]=-1;
		}
		for(int i=2;i<=100;i++)
			for(int j=1;j<=N;j++)
				if(gcd(i,A[j])==i) cnt[i]++;
		printf("%d ",dfs_optimaly(0,0));
		printf("%.4lf\n",dfs_randomly(0,0));
	}
	return 0;
}

  

●CodeChef Sereja and Game