博弈论
必败点(P点) :前一个选手(Previous player)将取胜的位置称为必败点。
必胜点(N点) :下一个选手(Next player)将取胜的位置称为必胜点。
必败(必胜)点的属性:
(1) 所有终结点是必败点(P点);
(2) 从任何必胜点(N点)操作,至少有一种方法可以进入必败点(P点);
(3)无论如何操作, 从必败点(P点)都只能进入必胜点(N点).
由上面的属性得到该题的算法:
步骤1:将所有终结位置标记为必败点(P点);
步骤2: 将所有一步操作能进入必败点(P点)的位置标记为必胜点(N点)
步骤3:如果从某个点开始的所有一步操作都只能进入必胜点(N点) ,则将该点标记为必败点(P点) ;
步骤4: 如果在步骤3未能找到新的必败(P点),则算法终止;否则,返回到步骤2。
hdu1848
题意:
取石子问题,一共有3堆石子,每次只能取斐波那契数个石子,先取完石子者胜利,问先手胜还是后手胜
从i个中取走 fib[1],fib[2],...,fib[j]<=i 个后剩下i-fib[1], i-fib[2],..., i-fib[j]个
他们的等价类数中没有出现的最小数就是i的等价类数
E[0]=0;
i=1,取走fib[1]=1个 i-fib[1]=0,0的等价类数是0,没有出现的最小数就是1
E[1]=1;
i=2,取走fib[1]=1个 i-fib[1]=1,取走fib[2]=2个,i-fib[1]=0, 1和0的等价类数是1,0,没有出现的最小数就是2
E[2]=2;
i=3,取走fib[1]=1个 i-fib[1]=2,取走fib[2]=2个 i-fib[1]=1,取走fib[3]=3个 i-fib[1]=0, 2,1和0的等价类数是2,1,0,没有出现的最小数就是3
E[3]=3;
i=4,取走fib[1,2,3]=1,2,3个 剩下3,2,1,没有出现的最小数就是0
E[4]=0; ( 4是必败点)
i=5,取走fib[1,2,3,4]=1,2,3,5个 剩下4,3,2,0,等价类数是 0,3,2,0没有出现的最小数就是1
E[5]=1;
这样就得到了一个等价类数数组。接着就运用那个定理,如果一开始就出现必败点,即(e[n] ^ e[m] ^ e[p]) == 0。那么按照最优走法则必输。其它情况必赢。

1 #include<stdio.h> 2 #include<string.h> 3 4 5 int f[100] = {1,2,3,5}; 6 int e[1001] = {0,1,2,3}; 7 int b[16]; 8 int main() 9 { 10 int n, i, j, m, p; 11 for( i = 3 ; f[i-1] <= 1000 ; i++)//允许拿的数目 12 { 13 f[i] = f[i -1] + f[i - 2]; 14 } 15 16 for( i = 4 ; i < 1001 ; i++) 17 { 18 e[i] = i; 19 memset(b,0,sizeof(b)); 20 for( j = 0 ; f[j] <= i ; j++) 21 b[e[i-f[j]]] = 1; //记录已经出现的数 22 /*f[j]是允许取走的个数,i-f[j]是剩下的个数,因为e[i]=i; 23 则b[]记录出现的数字,再找没有出现的最小数,就是下面的循环了*/ 24 25 for( j = 0 ; j < 15 ; j++)//找没有出现的最小数 26 { 27 if(b[j] == 0) 28 { 29 e[i] = j; 30 break; 31 } 32 } 33 } 34 35 while(scanf("%d%d%d", &n, &m, &p), (n!= 0 || m!= 0 || p!= 0 )) 36 { 37 if( (e[n] ^ e[m] ^ e[p]) == 0)//判断输赢 38 printf("Nacci\n"); 39 else 40 printf("Fibo\n"); 41 } 42 return 0; 43 }
转载请注明出处:http://www.cnblogs.com/ygdblogs