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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值