鑑於昨天做多校聯合的試題受的打擊太大,今天早上在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;
}