08-24 HDU2601求因數 HDU2604二分矩陣遞歸 USACO4.1.4D邊構圖DFS求環

鑑於昨天做多校聯合的試題受的打擊太大,今天早上在HDOJ掛了一套菜鳥賽的題目...雖然是菜鳥題目,但是對我這隻小菜鳥,還是很有收穫的


A題很水,陰在字符串讀取上..C題原始揹包..D題高中物理題,公式忘得差不多了..不過回憶了半天總算是過了,主要寫一下B題和E題吧


HDU 2601 http://acm.hdu.edu.cn/showproblem.php?pid=2601


B題是求N因數個數的題目,一開始直接用LogN的枚舉,過是過了..但是效率實在是太低了..

想到了分解質因數,N=a1^k1*a2^k2*a3*k3 則因數個數M=(a1+1)(a2+1)(a3+1).

但我寫的那個分解質因數效率實在不怎麼高,看了學長的程序,果然差距還是很大啊..雖然只是一個小優化,效率已經是天壤之別了..

#include <iostream>
using namespace std;
typedef long long LL;
LL fun(LL n)
{
    LL ans=1,i;
    for(i=2;i*i<=n;i++)
    {
        if(n%i==0)
        {
            int s=0;
            while(n%i==0)
            {
                n/=i;//改變N的大小 會使循環次數快速減小
                s++;
            }
            ans*=(s+1);
        }
    }
    if(n>1) ans*=2;//這裏注意一下
    return ans;
}
int main()
{
    LL n;
    int cas;
    scanf("%d",&cas);
    while(cas--){
        cin>>n;
        n++;
        cout<<(fun(n)-1)/2<<endl;
    }
    return 0;
} 


HDU 2604 http://acm.hdu.edu.cn/showproblem.php?pid=2604


E題是一題遞歸題,遞歸方程不難想出,但是效率也是很低啊..

請教學長,原來遞歸可以用二分矩陣優化,看了一下67大牛的文章,受益頗多.http://www.matrix67.com/blog/archives/276/

例如對於f(n) = 4f(n-1) - 3f(n-2) + 2f(n-4) ,右上n-1的矩陣裏對角線填1,第n行填遞推係數,然後利用結合律二分計算矩陣,

對於這一題,時間從4700MS+優化到了109MS,真的快了很多啊

矩陣相乘有不少需要注意的地方,

遞推方程F[N]=F[N-1]+F[N-3]+F[N-4]


#include <cstdio>
using namespace std;
int st[5][5]={
	{0,0,0,0,0},
	{0,0,1,0,0},
	{0,0,0,1,0},
	{0,0,0,0,1},
	{0,1,1,0,1},	
};
struct jz{
	int a[5][5];	
	//兩種構造方法,一種清0,一種是相乘的矩陣 
	jz(int k){
		if(k==0)for(int i=1;i<=4;i++)for(int j=1;j<=4;j++)a[i][j]=0;
		if(k==1)for(int i=1;i<=4;i++)for(int j=1;j<=4;j++)a[i][j]=st[i][j];	
	}
	//矩陣相乘,乘法過程中模m 
	jz mult(jz jz2,int m){
		jz ans(0);
		for(int i=1;i<=4;i++)
		for(int j=1;j<=4;j++)
		for(int k=1;k<=4;k++)
			ans.a[i][j]=(ans.a[i][j]+a[i][k]*jz2.a[k][j])%m;	
		return ans;
	}
	//得到結果 
	int getr(){
		return a[4][1]*2+a[4][2]*4+a[4][3]*6+a[4][4]*9;
	}
};
jz dfs(int k,int m){
	if(k==1)return jz(1);
	//二分 
	jz rs=dfs(k/2,m);
	jz ans=rs.mult(rs,m);
	//處理爲奇數的情況 
	if(k%2==1){
		jz t2=jz(1);
		jz tmp=ans.mult(t2,m);	
		ans=tmp;
	}
	return ans;
} 

int main(){
	int l,k;
	int a[5]={1,2,4,6,9};
	while(scanf("%d%d",&l,&k)!=EOF){
		if(l<=4)printf("%d\n",a[l]%k);
		else{
			jz ans=dfs(l-4,k);
			printf("%d\n",ans.getr()%k);	
		}	
	}	
}



USACO 4.1.4 Fence Loopshttp://www.nocow.cn/index.php/Translate:USACO/fence6


單純的找最小環不難,用DFS就可以了,這一題難在告訴你的是邊之間的關係,而沒有點..看了NOCOW上大牛的思路,用邊構圖,牛啊..要注意順序,只能從邊的這邊進,然後從邊的另一邊出,存儲的時候該邊兩端的邊要分開儲存,用一個dir數組表示兩條邊之間的關係..然後搜索的時候注意方向..

/*
ID: swm80232
PROG:fence6
LANG: C++
*/
#include <cstdio>
#include <string>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
using namespace std;
int n,l[105][2][10],vis[105],res,dir[105][105],w[105]; 
void dfs(int s,int p,int len,int d){
	if(len>=res)return;
	for(int i=1;i<=l[p][1-d][0];i++){//搜這個點的另一端 
		int pi=l[p][1-d][i];
		if(pi==s&&dir[s][p]==0){//一開始是從s->1端開始搜,所以最後結束是在s->0端纔是環 
			res=min(res,len);
			return;	
		}	
		if(!vis[pi]){
			vis[pi]=1;
			dfs(s,pi,len+w[pi],dir[pi][p]);//看p在現在點的哪一邊(下次搜從另一邊開始搜) 
			vis[pi]=0;	
		} 
	}	
}
int main(){
    freopen("fence6.in","r",stdin);
    freopen("fence6.out","w",stdout);
    memset(w,0,sizeof w);
    
    int s,ls,n1s,n2s;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d%d%d%d",&s,&ls,&n1s,&n2s);
		w[s]=ls;
		l[s][0][0]=n1s;
		l[s][1][0]=n2s;
		for(int j=1;j<=n1s;j++){
			scanf("%d",&l[s][0][j]);
			dir[s][l[s][0][j]]=0;
		}
		for(int j=1;j<=n2s;j++){
			scanf("%d",&l[s][1][j]);
			dir[s][l[s][1][j]]=1;
		}
	}
		
	res=1e8;
	memset(vis,0,sizeof vis);
	for(int i=1;i<=n;i++){
		if(w[i]==0)continue;
		//memset(vis,0,sizeof vis); 
		vis[i]=1;		
		dfs(i,i,w[i],0);//從s->該點爲0端開始搜 
		vis[i]=0;	
	} 
	printf("%d\n",res);
   // system("pause");
    return 0;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章