intfree () 2001-10-30 18:49:59 在 專題開發/技術/項目 / 資料結構與演算法 提問
一道中學競賽的題目:
有L個球,1..L編號,兩個人輪流從中取球
每人每次可以(必須)取“編號連續的”a個或b個或c個球
直到不能取為止,最後一個取球的人獲勝
給出a, b, c, L,問先取者有無必勝策略
其中1<=a,b,c<=L<=500,
a,b,c都為偶數且L為奇數,或者a,b,c都為奇數且L為偶數
我的一些結論:
若a=b=c,則
a<=L<3a-1時,先取必勝
L=3a-1時,後取必勝
3a<=L<5a-1時,先取必勝
L=5a-1時,後取必勝
5a<=L<9a-3時,先取必勝
L=9a-3時,後取必勝
L更大我就不知道了;若a,b,c不相等,我也就不知道怎麼做了。
我的一個同學說,正確的演算法可以在64K記憶體,1s內出解,可我怎麼也想不出。
懇請各位高手幫忙。
mathe () 回複於 2001-11-05 09:17:09 得分 0
//I have solved the problem now, the speed is very quick.
#define M 512
ASSERT(L<M);
ASSERT(a>0&&b>0&&c>0);
int m[M];
for(i=0;i<=L;i++){
int n[M];
memset(n,0,sizeof(n));
if(i-a>=0){
for(j=0;j<=(i-a)/2;j++)n[m[j]^m[(i-a-j)]]=1;
}
if(i-b>=0){
for(j=0;j<=(i-b)/2;j++)n[m[j]^m[(i-b-j)]]=1;
}
if(i-c>=0){
for(j=0;j<=(i-c)/2;j++)n[m[j]^m[(i-c-j)]]=1;
}
for(j=0;j<M;j++)
if(n[j]==0)break;
m[i]=j;
}
if(m[L]==0){
printf("The first player will lost/n");
}
else
{
printf("The first player will win/n");
}
mathe () 回複於 2001-11-05 09:39:52 得分 15
先手在m[L]<>0時,必勝的策略是:
在任何一次對手選擇(或者第一次)以後,
剩餘為k組連續資料,長度分別為a(1),a(2),...,a(k).
這時必有m[a(1)]^m[a(2)]^...^m[a(k)]==u!=0.
設u的最高位元位為s,即pow(2,s)<=u<pow(2,s+1),必然有i(1<=i<=k)使得(m[a(i)]&(1<<s))!=0,(即至少有一個對應的m,第s為不為0。) 所以m[a(i)]>(m[a(i)]^u).
第i組資料長度為a(i),那麼我們必定可以有一種方案使得使用這個方案後,第i組新產生兩組
數(或一組)對應m的異或為(m[a(i)]^u)。也就是可以將是變換後
m[a(1)]^m[a(2)]^...^m[a(k)]==0.
我們需要證明的是對於狀態0,任意取球後狀態非零;而非零的狀態,總可以有取球方案將狀態恢複的0。
通過上面的程式定義的狀態有下面的性質,如果狀態i<狀態j,那麼總有取球方案,從狀態j變到狀態i.
在得出上面的性質中,我還需要證明一個比較有趣的數學題:
如果i<>j,則
任何小於i^j的資料都出現在先面的集合中
{i^0,i^1,...,i^(j-1), j^0,j^1,...,j^(i-1)}
當然,i^j 不出現在這個集合中。