题目描述
输入
输入的第1行有两个正整数a和b。
输出
若找到的a和b之间约数个数最多的数是x,则输出div(x)。
样例输入
1 36
样例输出
9
#include <cstdio>
#include <cstring>
#include <cmath>
int const MAX = 100005;
int prime[MAX];
int ma, cnt, l, r;
void get_prime()
{
bool get[MAX];
memset(get, true, sizeof(get));
get[0] = get[1] = false;
for(int i = 2; i <= sqrt(MAX); i++)
if (get[i])
for(int j = i * i; j <= MAX; j += i)
get[j] = false;
for(int i = 2; i <= MAX; i++)
if(get[i])
prime[++cnt] = i;
}
void search(int from, int tot, int num, int left, int right)
{
ma = tot > ma ? tot : ma;
if((left == right) && (left > num))
search(from, tot * 2, num * left, 1, 1);
for(int i = from; i <= cnt; i++)
{
if (prime[i] >right)
return;
else
{
int j = prime[i], x = left - 1, y = right, n = num, t = tot, m = 1;
while(true)
{
m ++;
t += tot;
x /= j;
y /= j;
if (x == y)
break;
n *= j;
search(i + 1, t, n, x + 1, y);
}
if (tot < (ma / (1 << m)))
return;
}
}
}
int main()
{
cnt = 0;
get_prime();
while(scanf("%d %d", &l, &r) != EOF)
{
if((l == 1) && (r == 1))
ma = 1;
else
{
ma = 2;
search(1, 1, 1, l, r);
}
printf("%d\n", ma);
}
}
B题 Square
| Time Limit: 3000MS | Memory Limit: 65536K | |
| Total Submissions: 19982 | Accepted: 6955 |
Description
Input
Output
Sample Input
3 4 1 1 1 1 5 10 20 30 40 50 8 1 7 2 6 4 4 3 5
Sample Output
yes no yes
Source
题目链接 :http://poj.org/problem?id=2362
题目大意 :有n个木条,问用这n个木条能不能拼出一个正方形
题目分析 :DFS+剪枝,这题是POJ 1011 那题的简单版,而且这题时间放的很宽,基本上小剪一下就可以过,笔者本着学习的态度写了不少剪枝,依旧跑了110ms,不知0ms大神是怎么做到的,若有大神知道还求指点
1.总长必须是4的倍数,否则无解
2.对木条从大到小排序,如果最长边大于总长的四分之一则无解,而且每次从长边开始选可以减少判断次数,因为长边的灵活度比较低,如果有一根长边用不上则无解
3.搜索时如果两根木条的长度相同,前一根没有选则后一根必然不会选
4.每次加之前判断加上是否会超过边长,若会超过则不选
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int const MAX = 21;
int stick[MAX];
int vis[MAX]; //标记木条是否用过
int num, side; //num表示木条数,side表示边长
bool flag;
//cur表示当前正在拼装的木条长度,other表示其他的木条,count表示拼成的边数
bool cmp(int a, int b)
{
return a > b;
}
bool DFS(int cur, int other, int count)
{
//如果当前长度等于边长,count加1,当前长和其他都置为0开始拼下一条边
if(cur == side)
{
count++;
cur = 0;
other = 0;
}
//如果count等于4,说明找到可行解,返回true
if(count == 4)
return true;
for(int i = other; i < num; i++) //枚举其他木条
{
//若两个木棒长度相同,前一个没用,则这一个也不会用
if(i && !vis[i-1] && stick[i] == stick[i-1])
continue;
//如果下一根未用且加上后长度小于边长
if(cur + stick[i] <= side && !vis[i])
{
vis[i] = 1; //标记为已使用
//如果当前方案能找到可行解则返回true
if(DFS(cur + stick[i], i + 1 , count))
return true;
//如果找不到,将改木条标记为未使用,供下一种方案使用
vis[i] = 0;
}
}
return false;
}
int main()
{
int n, sum;
scanf("%d",&n);
while(n--)
{
flag = false;
sum = 0;
memset(vis,0,sizeof(vis)); //木条初始化为未用
scanf("%d",&num);
for(int i = 0; i < num; i++)
{
scanf("%d",&stick[i]);
sum += stick[i];
}
sort(stick, stick+num, cmp); //将木条从小到大排序
//如果总长不是4的倍数或者最长边大于总长的四分之一则不可能拼成
if(sum % 4 || stick[0] > sum / 4)
{
printf("no\n");
continue;
}
side = sum / 4;
flag = DFS(0,0,0);
if(flag)
printf("yes\n");
else
printf("no\n");
}
}
C题 Sticks
| Time Limit: 1000MS | Memory Limit: 10000K | |
| Total Submissions: 120211 | Accepted: 27813 |
Description
Input
Output
Sample Input
9 5 2 1 5 2 1 5 2 1 4 1 2 3 4 0
Sample Output
6 5
Source
题目链接 : http://poj.org/problem?id=1011
题目大意 :给若干根切断的木条,用完全部所给木条拼成n根等长的原始木条,求所拼原始木条的最短长度
题目分析 :DFS + 剪枝 ,本题限制时间为1000ms,剪枝不到位绝对超时,下面列出几条不可少的剪枝
1. 设有n根木条枚举能拼成的长度为sum/n~1,这里n必须是sum的约数否则无解,找到了则为最优解,因为均分的越多长度就越短
2.按长度从大到小排序,最长的一根长度必然小于sum/n,否则无解,从长的开始取,因为长的灵活度比较低,之后便于剪枝。
3.若搜索时某两根的长度相同,第一根没取那第二根也不会取
4.每次取的木条加上取之前的长度要小于sum/n
5.之前的POJ2362 http://blog.youkuaiyun.com/tc_to_top/article/details/38460259 没加这个剪枝跑的110ms加上后0ms飘过,这题则必须加这条剪枝 :如果当前长度cur为0,当前取的最长木条为stick[i],结果stick[i]拼接失败则我们可以直接退出搜索,当前情况无解,因为如果某次当前最长的木条没被选上,则之后木条数更少了它更不会被选上,因为我们是按照降序排列的
这里再给一组数据:
64
40 40 30 35 35 26 15 40 40 40 40 40 40 40 40 40 40 40 40 40 40
40 40 43 42 42 41 10 4 40 40 40 40 40 40 40 40 40 40 40 40 40
40 25 39 46 40 10 4 40 40 37 18 17 16 15 40 40 40 40 40 40 40 40
答案: 454
这组数据用我的0ms过的代码跑了3,4秒,只能说POJ数据略水,该组数据要有1秒内过的求教
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int const MAX = 64;
int vis[MAX];
int stick[MAX];
int num, one, div_num;
bool cmp(int a, int b)
{
return a > b;
}
//cur代表当前某根木条的拼接长度,other代表未用木条,count代表拼成的根数
bool DFS(int cur, int other, int count)
{
if(count == div_num) //若拼成的根叔等于份数,则找到返回
return true;
for(int i = other; i < num; i++)
{
//若当前供选长度与前一个相等且前一个未使用则不考虑当前这根
if(i && !vis[i-1] && stick[i] == stick[i-1])
continue;
if(cur + stick[i] <= one && !vis[i])
{
//若加上小于原始长度将其标记为已使用继续搜索
if(cur + stick[i] < one)
{
vis[i] = 1;
if(DFS(cur + stick[i], i + 1, count))
return true;
//若之前方案不可行,将当前这根标记为未使用
vis[i] = 0;
//这是个很重要的剪枝,若当前选择的最长长度得不到解,该方案必然无解
if(cur == 0)
return false;
}
//若加上等于原始长度将其标记为已使用拼成一根,开始拼下一根
if(cur + stick[i] == one)
{
vis[i] = 1;
if(DFS(0, 0, count + 1))
return true;
//若之前方案不可行,将当前这根标记为未使用
vis[i] = 0;
//这是个很重要的剪枝,若之前的DFS(0,0,count+1)有一个不满足
//则该方案必然无解
return false;
}
}
}
return false;
}
int main()
{
int sum;
while(scanf("%d",&num) != EOF && num)
{
sum = 0;
memset(vis,0,sizeof(vis));
for(int i = 0; i < num; i++)
{
scanf("%d",&stick[i]);
sum += stick[i];
}
sort(stick, stick+num, cmp); //从大到小排序
//从份数最多的枚举起,则找到一组解就可以退出,因为它必为最短
for(int i = num; i > 0; i--)
{
if(sum % i == 0) //每根原始木条的长度必须是整数
{
one = sum / i; //一根原始木条的长度
//若当前的原始木条短于最长的切断的木条长度则原始木条值非法
if(stick[0] > one)
continue;
else
{
div_num = i; //份数
if(DFS(0,0,0)) //找到一个可行解就退出
break;
}
}
}
printf("%d\n",one);
}
}

本文深入探讨了编程中的深度搜索技术及其剪枝优化策略,以解决复杂问题的高效方法。重点分析了三个典型问题:求区间内含有最多约数的整数、使用特定长度木条构建正方形以及根据切断木条恢复原始等长木条的最短长度。通过详细算法描述、步骤解释和关键剪枝技巧,本文提供了快速解决问题的实用指南。
1万+

被折叠的 条评论
为什么被折叠?



