第一题:http://acm.hdu.edu.cn/showproblem.php?pid=2018
这个题目是可以递归求解的,但是递归调用函数是需要时间的,并且递归会计算一些重复的东西,所以可以考虑记忆话递归。这样的话就不用重复的去计算了。然后对于每一个数字的结果都是差不多的。可以预处理用数组保存。
a[1] = 1; ans[2] = 2; ans[3] = 3;
for(int n = 4; n <= 55; n++)
{
ans[n] = ans[n-1] + ans[n-3];
}
第二题:http://acm.hdu.edu.cn/showproblem.php?pid=2035
这个题目实际上是 快速幂 的简单应用,我尝试说一下大概意思.
求a^b.
如果b是偶数,那么a^b = a^(b/2)^2 = (a*a)^(b/2);
如果b是奇数,那么 a^b = a*a^(b-1),令c = b-1。 c就是偶数了,和上面的一样.
int mod_pow(int x, int n) // 求x^n
{
int res = 1;
while(n)
{
if(n&1) res = res*x%mod; // 一般结果都比较大,所以有一个取余
x = x*x%mod;
n >>= 1;
}
return res;
}
第三题:http://acm.hdu.edu.cn/showproblem.php?pid=2050
这是一个数学题。画图,想要得到最多的划分区间,就是让每一条线和尽可能多的线相交。提前说明,我画图蛇皮怪。
只有1对折线的时候,就是2, 2对7, 3对16。这是有道理的。如果不是折线(不是相交止于一个端点),是可以很快的发现可以划分的区间数是 3*n*n. 有了端点以后减少了n-1个端点,所以答案就是 3*n*n-(n-1) = 3*n*n-n+1;
第四题:http://acm.hdu.edu.cn/showproblem.php?pid=2089
这个题目好玩。如果不考虑时间的问题的话,是可以直接暴力的的去枚举区间里面的每一个数,然后取判断每一个数是不是含有62的。这样对于这道题目来说是可以的。但是这个题目,想一想,一个数里面有没有62和输入的区间有没有关系。显然是没有关系的。因此有没有62就是数的特性,就好比一个数是不是奇数一样,是数的一种特性,和需要求的区间是没有关系的。那么这种题目一般是用数位DP来解是比较好玩的。先给一般解法的代码。
/*
time :795ms memory: 1200KB
*/
#include<stdio.h>
bool f(int x) //这个函数就是判断这个数是不是含有62
{
while(x)
{
int n = x%10;
if(n == 2 && x%100 == 62 || n == 4) return false;
x = x/10;
}
return true;
}
int main()
{
int n, m, sum = 0;
while(scanf("%d%d", &n, &m) != EOF && n != 0 || m != 0)
{
for(int i = n; i <= m; i++)
{
if(f(i)) sum++;
}
printf("%d\n", sum);
sum = 0;
}
return 0;
}
下面是优化了的,相对于而言就好一点。
/*
time:93ms memory:5312KB
*/
#include<iostream>
#include<string.h>
#include<cstdlib>
using namespace std;
int ans[1000010];
int fun(int a)
{
char str[10010]; // 这里是不需要开辟这么大的字符串的,想想最大的32 Int 2147483647,才10位,开辟12就可以了。// char str[12];
itoa(a, str, 10); //将a以10进制的形式放在str字符串里面.
for(int i = 0; i < strlen(str); i++)
if(str[i] == '4') return 0;
for(int i = 0; i < strlen(str) - 1; i++)
if(str[i] == '6' && str[i+1] == '2') return 0;
return 1;
}
int main()
{
int m, n; ans[0] = 1;
for(int i = 0; i <= 1000000; i++)
ans[i] = ans[i-1] + fun(i);
while(cin >> m >> n)
{
if(m == 0 && n == 0) break;
cout << ans[n] - ans[m] + fun(m) << endl;
}
return 0;
}
这个题目好玩,所以还有一种解法, 上面已经有了数位DP的想法了,但是还并不是的。
/*
time: 0s memory:1676KB
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
using namespace std;
typedef long long ll;
int a[20];
int dp[20][2];
int dfs(int pos, int pre, int sta, bool limit)
{
if(pos == -1) return 1;
if(!limit && dp[pos][sta] != -1) return dp[pos][sta];
int up = limit ? a[pos] : 9;
int tmp = 0;
for(int i = 0; i <= up; i++)
{
if(pre == 6 && i == 2)continue;
if(i == 4) continue;//都是保证枚举合法性
tmp += dfs(pos-1, i, i == 6, limit && i == a[pos]);
}
if(!limit) dp[pos][sta] = tmp;
return tmp;
}
int solve(int x)
{
int pos = 0;
while(x)
{
a[pos++] = x%10;
x /= 10;
}
return dfs(pos-1, -1, 0, true);
}
int main()
{
int le, ri;
while(~scanf("%d%d", &le, &ri) && le + ri)
{
memset(dp, -1, sizeof (dp));
printf("%d\n",solve(ri) - solve(le-1));
}
return 0;
}
第五题:http://acm.hdu.edu.cn/showproblem.php?pid=2007
虽然是一个很简答的题目。因为没有给定数据的范围,所以不太好做预处理。用平常的解法就好了。如果给定的数据的范围的话,先求出[1, n]的和,然后查询[a, b]就直接 sum[b]- sum[a] 就可以了。
第六题:http://acm.hdu.edu.cn/showproblem.php?pid=2013
这个题目和第五题类似,好一点就是给定了数据范围,就可以考虑进行预处理了。答案就是a[n]
a[31] = { 0, 1 };
for (i = 2; i < 31; i++)
{
a[i] = 2 * ( a[i - 1] + 1);
}
第七题:http://acm.hdu.edu.cn/showproblem.php?pid=2019
这就是一个比较简单的插排的思想,一般就是找一下位置,后移一下,插一下。找位置用二分查找,会好一点,后移和插入用链表好一点。但是二者不可兼得,链表不支持二分。对于这个题目。怎么玩都可以。建议使用二分去查找,找到了以后,保存一个位置,然后先输出前面的,然后输出待查找的值,然后输出后面的。也就是说不后移也不插进去,可以这样做的原因就是利用了评测机制。只要输出的是一样的,就可以了,但是如果是自己的做课程设计和项目的时候,还是要找到位置,后移,插入。