POJ 1012 Timing Limit Exceed問題

Description

The Joseph's problem is notoriously known. For those who are not familiar with the original problem: from among n people, numbered 1, 2, . . ., n, standing in circle every mth is going to be executed and only the life of the last remaining person will be saved. Joseph was smart enough to choose the position of the last remaining person, thus saving his life to give us the message about the incident. For example when n = 6 and m = 5 then the people will be executed in the order 5, 4, 6, 2, 3 and 1 will be saved. 

Suppose that there are k good guys and k bad guys. In the circle the first k are good guys and the last k bad guys. You have to determine such minimal m that all the bad guys will be executed before the first good guy. 

Input

The input file consists of separate lines containing k. The last line in the input file contains 0. You can suppose that 0 < k < 14.

Output

The output file will consist of separate lines containing m corresponding to k in the input file.

Sample Input

3
4
0

Sample Output

5
30

       

 在看到POJ 1012這個題之後,首先想到這個問題就是容易,直接用循環查詢,就可以啦。於是就寫出了下面的程序,誰知道提交竟然會去超時。。。


#include<stdio.h>
#include<string.h>

#define MAX 28

int main(void){
    int k,kk,i,m,count=0,countBad=0;
    int c[MAX];
    scanf("%d",&k);
    while(k){
	m=k+1;
	kk=2*k;
	countBad=0;
	count=0;
	memset(c,0,sizeof(c));
	for(i=1;i<=kk;i=(++i)%(kk+1)==0?1:i){	//這裏表達式3,有點亂。其實,若是以數組下標0開始對人編號,在對有取模運算的表達式時會很方便
	    if(countBad==k)
		break;
	    if(c[i]==1)
		continue;
	    count++;
	    if(count==m && i<=k){
		m++;
		count=0;countBad=0;
		memset(c,0,sizeof(c));
		i=0;		
		continue;
		}
	    else if(count==m){
		c[i]=1;
		countBad++;
		count=0;
	   }
	}
	printf("%d\n",m);
	scanf("%d",&k);
    }
    return 0;
}


真的會超時的,在算k=10,11時候,在自己的電腦上運行真的需要很長一段時間。

分析上面的算法複雜度:

普通的Joseph環問題,有n個人,開報數m的話,只剩下最後一個人的算法複雜度爲:(n-1)*m,這個算法若不是找出最後一個人,若出第k個人的算法複雜度應該是k*m.

所以Joseph在找出第k個人的時候,是和這個環的長度是無關的。


而在上面代碼算法中,m是不確定的,我們從m=k+1開始,找出k個人,和算法複雜度爲

  (k-1)*m+(k-1)*(m+1)+...+(k-1)*(m+i)+...+>=  (k-1)*m+(k-1)*(m+1)+...+(k-1)*(m+i)+...+k*(m+w)  ,前面用k-1標記,說明我們在後面找不k個bad guys。

>=(k-1)[ m+(m+1)+....+(m+w)]=(k-1)[(w+1)m+w(w+1)/2)]=O(kwm+kw^2/2) , 因爲w是不確定的,所以這個算法複雜度是Ω(n)=kw^2. 記號(Ω是>=的意思

在上面程序運行進,當k=1,..13時,w對應的值爲27530169441187276321740933134599011358657,  2504881 }-k;

對於2504881^2這樣的時間複雜度,確實是很長很長啦。

所以利用枚舉算法來解決該問題顯然是不行的。所以我們應該用數學的方法來解決該問題。

其實Joseph Problem是有一個遞推公式的:‘

環中有n個人,從0開始編號(0,1, ......,n-1)   依次報數m

            第i輪出局的人爲f(i)=(f(i-1)+m-1)%(n-i+1),f(0)=0; (參考wikipedia)

注意:第i輪出局的人f(i)返回的不是他在第一輪所在編號,而在第i輪,刪除i-1人之後的下標。

如(0,1,2,3,4,5)這6個人報5的話,其下標依次爲第一輪:              人        1,2,3,4,5,6

 下標編號:(0,1,2,3,4,5)從第0號開始報數,出局的爲5,其編號爲f(1)=4 。

       第二輪                人  :    1,2,3,4,6

 下標編號:(0,1,2,3,4)從f(1)=4編號開始報數,從出局的爲4,其編號爲f(2)=3。

        第三輪                人  :    1,2,3,6

 下標編號:(0,1,2,3)從f(2)=3編號開始報數,從出局的爲6,其編號爲f(3)=3。

這樣可以看出,利用上公式的話,只要找出的前k輪找出的下標大於k就OK了。

利用數學公式,而不是利用枚舉的方法,這時的時間複雜度爲:我們不能找出其上限,但是能找出其下線,是Ω(n)=kw^2. 記號(Ω是>=的意思)


代碼如下:

#include<stdio.h>

int query(int k,int m);

int main(void){
    int k,m;
    scanf("%d",&k);
    while(k){
	m=k+1;
	while(1){
	    if(query(k,m))
		break;
	    if(query(k,m+1)){
		m++;break;
	     }
	    m+=(k+1);
	}
	printf("%d\n",m);
	scanf("%d",&k);
    }
    return 0;
}

int query(int k,int m){
    int i,f=0,n=k<<1;
    for(i=0;i<k;i++){
	f=(m+f-1)%(n-i);
	if(f<k)
	    return 0;
    }
    return 1;
}

提交後,還會超時。


可能是因爲第次輸入k時,都會重新計算。所以解決方法,將1--13的先計算好,結果保存在數組中。然後有重複的k的時候,直接查找數組就可以了,而不需要重新計算了。

這下就會超時了,代碼如下:

#include<stdio.h>
//這樣都會超時,難道要一次運行完成,把全部運算出來,存在數組中。這樣
//要是者不行的話,那隻能用直接打表的方法了。`
int query(int k,int m);

int main(void){
    int i,k,m;
    int a[13];
    for(k=1;k<=13;k++){
	m=k+1;
	while(1){
	    if(query(k,m)){
		a[k-1]=m;
		break;
	    }
	    if(query(k,m+1)){
		a[k-1]=m+1;
		break;
	     }
	    m+=(k+1);
	}
    }
    while(scanf("%d",&k),k){
    	printf("%d\n",a[k-1]);
    }
    return 0;
}

int query(int k,int m){
    int i,f=0,n=k<<1;
    for(i=0;i<k;i++){
	f=(m+f-1)%(n-i);
	if(f<k)
	    return 0;
    }
    return 1;
}








發佈了39 篇原創文章 · 獲贊 2 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章