1. 程式人生 > >HFOI2017.7.13校內賽(普及組)題解

HFOI2017.7.13校內賽(普及組)題解

T1:分解因數
描述
    給出一個正整數a,要求分解成若干個正整數的乘積,即a = a1 * a2 * a3 * ... * an,並且1 < a1 <= a2 <= a3 <= ... <= an,問這樣的分解的種數有多少。注意到a = a也是一種分解。
輸入
    第1行是測試資料的組數n,後面跟著n行輸入。每組測試資料佔1行,包括一個正整數a (1 < a < 32768)
輸出
    n行,每行輸出對應一個輸入。輸出應是一個正整數,指明滿足要求的分解的種數
樣例輸入
    2
    2
    20
樣例輸出
    1

    4

定義f(k,b)函式,表示待分解的數字為k,目前最大的非1因數為i。

遞迴求解。

程式碼:

#include<cstdio>
int f(int k,int b)
{
	int ans=0,i;
	if(k==1)return 1;
	for(i=b;i<=k;i++)
	{
		if(k%i==0)
		{
			k/=i;
			ans+=f(k,i);
			k*=i;
		}
	}
	return ans;
}
int main()
{
	int n,i,k;
	scanf("%d",&n);
	for(i=1;i<=n;i++)
	{
		scanf("%d",&k);
		if(k==1)printf("1\n");
		else printf("%d\n",f(k,2));
	}
}
T2:單詞序列
描述
    給出兩個單詞(開始單詞和結束單詞)以及一個詞典。找出從開始單詞轉換到結束單詞,所需要的最短轉換序列。轉換的規則如下:
    1、每次只能改變一個字母
    2、轉換過程中出現的單詞(除開始單詞和結束單詞)必須存在於詞典中
    例如:
    開始單詞為:hit
    結束單詞為:cog
    詞典為:[hot,dot,dog,lot,log,mot]
    那麼一種可能的最短變換是: hit -> hot -> dot -> dog -> cog,
    所以返回的結果是序列的長度5;
    注意:
    1、如果不能找到這種變換,則輸出0;
    2、詞典中所有單詞長度一樣;
    3、所有的單詞都由小寫字母構成;
    4、開始單詞和結束單詞可以不在詞典中。
輸入
    共兩行,第一行為開始單詞和結束單詞(兩個單詞不同),以空格分開。第二行為若干的單詞(各不相同),以空格分隔開來,表示詞典。單詞長度不超過5,單詞個數不超過30。
輸出
    輸出轉換序列的長度。
樣例輸入
    hit cog
    hot dot dog lot log
樣例輸出
    5

正解是bfs暴搜(誒都是正解了還是什麼暴搜)。。。

考完試補了一個正解程式碼:

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=31;
struct Node{int d;string s;Node(int d,string s):d(d),s(s){}};
int vis[31];
string ss[maxn],s,t;
int main()
{
	cin>>s>>t;
	int n=1,l=s.length();
	while(cin>>ss[n]) n++;n--;
	queue<Node>q;
	q.push(Node(0,s));
	while(!q.empty())
	{
		Node x=q.front();q.pop();
		for(int i=0;i<l;i++)
		{
			for(char ch='a';ch<='z';ch++)
			if(ch!=x.s[i])
			{
				char tmp=x.s[i];
				x.s[i]=ch;
				if(x.s==t)
				{
					printf("%d\n",x.d+2);
					return 0;
				}
				for(int j=1;j<=n;j++)
				if(x.s==ss[j])
				{
					if(!vis[j])
					q.push(Node(x.d+1,x.s));
					vis[j]=1;
				}
				x.s[i]=tmp;
			}
		}
	}
	printf("0\n");
	return 0;
}
然而考試時不知道為什麼腦子短路寫了一個floyd,竟然過了,3ms。。。

【程式碼不堪入目】

#include<cstdio>
inline void f(int&m,int u){u<m&&(m=u);}
const int inf=1e6;
inline int h(char*s,char*t)
{
	int ret=0;
	while(*s)ret+=*(s++)!=*(t++);
	return ret>1?inf:ret;
}
char s[35][9];
int dis[35][35];
int main()
{
	int n;
	for(n=0;~scanf("%s",s[n]);n++);
	for(int i=0;i<n;i++)for(int j=0;j<n;j++)dis[i][j]=h(s[i],s[j]);
	for(int k=0;k<n;k++)for(int i=0;i<n;i++)for(int j=0;j<n;j++)f(dis[i][j],dis[i][k]+dis[k][j]);
	printf("%d\n",dis[0][1]==inf?0:dis[0][1]+1);
	return 0;
}
好吧就是定義h函式為從字串s到字串t的長度,然後直接floyd暴算。。。

20行程式碼強行AC

T3:一個人的旅行
Problem Description
雖然草兒是個路痴(就是在杭電待了一年多,居然還會在校園裡迷路的人,汗~),但是草兒仍然很喜歡旅行,因為在旅途中 會遇見很多人(白馬王子,^0^),很多事,還能豐富自己的閱歷,還可以看美麗的風景……草兒想去很多地方,她想要去東京鐵塔看夜景,去威尼斯看電影,去陽明山上看海芋,去紐約純粹看雪景,去巴黎喝咖啡寫信,去北京探望孟姜女……眼看寒假就快到了,這麼一大段時間,可不能浪費啊,一定要給自己好好的放個假,可是也不能荒廢了訓練啊,所以草兒決定在要在最短的時間去一個自己想去的地方!因為草兒的家在一個小鎮上,沒有火車經過,所以她只能去鄰近的城市坐火車(好可憐啊~)。
Input
輸入資料有多組,每組的第一行是三個整數T,S和D,表示有T條路,和草兒家相鄰的城市的有S個,草兒想去的地方有D個;
接著有T行,每行有三個整數a,b,time,表示a,b城市之間的車程是time小時;(1=<(a,b)<=1000;a,b 之間可能有多條路)
接著的第T+1行有S個數,表示和草兒家相連的城市;
接著的第T+2行有D個數,表示草兒想去地方。
Output
輸出草兒能去某個喜歡的城市的最短時間。
Sample Input
6 2 3
1 3 5
1 4 7
2 8 12
3 8 4
4 9 12
9 10 2
1 2
8 9 10
Sample Output
9

典型的最短路問題(無向圖),floyd過不了【廢話】,dijkstra演算法78ms(我的),膜拜31ms大神lyb和zjc。

程式碼:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1005,n=1001,INT=0x3f3f3f;//INT是int型別單個位元組的最大值,方便memset
int mp[maxn][maxn],st[maxn],en[maxn],distanse[maxn];//mp是地圖,st是臨近城市,en是目標城市,distanse儲存起始點到每一個點的距離
bool found[maxn];//判重函式
int t,s,d,a,b,w,tmp;
inline void input()//輸入函式
{
    int tot=t;
    while(tot--)
    {
        scanf("%d%d%d",&a,&b,&w);
        if(w<mp[a][b])mp[a][b]=mp[b][a]=w;
    }
    for(int i=0;i<s;i++)scanf("%d",&st[i]);
    for(int i=0;i<d;i++)scanf("%d",&en[i]);
}
inline int choose()//選擇一條權值最短的邊(通向新節點)
{
    int minn=INT;
    int cnt=-1;
    for(int i=1;i<1001;i++)
    {
        if(!found[i]&&distanse[i]<minn)
        {
            minn=distanse[i];
            cnt=i;
        }
    }
    return cnt;
}
void dijkstra(int v)//dijkstra演算法主過程
{
    for(int i=0;i<maxn;i++)
    {
        distanse[i]=mp[v][i];
        found[i]=0;
    }
    found[v]=1;
    for(int i=0;i<n;i++)
    {
        int p=choose();
        if(p==-1)return;
        found[p]=1;
        for(int j=0;j<n;j++)if(!found[j])if(distanse[p]+mp[p][j]<distanse[j])
        distanse[j]=distanse[p]+mp[p][j];//鬆弛操作
    }
}
int main()
{
    while(scanf("%d%d%d",&t,&s,&d)!=EOF)//處理多組資料
    {
        tmp=INT;
        memset(mp,INT,sizeof mp);//初始化
        input();
        for(int i=0;i<s;i++)
        {
            dijkstra(st[i]);
            for(int j=0;j<d;j++)tmp=min(tmp,distanse[en[j]]);
        }
        printf("%d\n",tmp);
    }
    return 0;
}