(拉丁语)Ad Hoc:为某种目的而特别设计的。
在程序设计竞赛的试题中,有这样一类试题,解题不能套用现成的算法,也没有模式化的求解方法,而是需要编程者自己设计算法来解答试题,这类试题被称作Ad Hoc类试题,也被称为杂题。实现的程序一般比较简短。
求解Ad Hoc类问题的两类方法:
机理分析法,就是根据客观事物的特性,分析其内部的机理,弄清其内在的关系,在适当抽象的条件下,得到可以描述事物属性的数学工具。
解题思路:
首先读懂题意,并总结归纳出解决本题的数学方法,可以得出“本题是求一个n,使得 n ! < = 2 k − 1 n! <= 2^k-1 n!<=2k−1”,这里的k是处理器的位数。
如此一来就有了解决本题的一个方法,接着考虑该数学式子是否可行。由于阶乘过大会溢出,因此我们可以考虑采用对数运算,即对不等式两边同时取对数,然后求解。
代码示例:
#include
#include
int main(){
int n;
while(~scanf("%d",&n) && n){
int k = (n-1960)/10,tmp = 4;
for(int i = 1;i <= k;i++) tmp *= 2;
//printf("%d\n",tmp);
double b = tmp ,ans = 0;
for(int i = 2;i <= tmp;i++){
double delta = log2(i);
// printf("%f %f %f\n",ans,delta,b);
if(ans+delta-b >= 0){
printf("%d\n",i-1);
break;
}
ans += delta;
}
}
return 0;
}
解题思路:
经过分析,如果想让最慢的两个人过桥(让且只让最慢的两个人过桥,其他人位置不变),显然有两种合理的解决方案;这两种方案在两种不同情况下都是正确的,但是若依据速度的值来分类讨论就太过于复杂了,因此我们可以每次比较两种方案“将最慢的两个人送过桥”,选取二者中较优的一种,可以证明这样的选取策略一定能得到最优解。
当然,这种策略最终会遇到的情况是:还剩2个人或3个人未过桥(剩余人数 < 4),此时特殊处理一下即可。
设当前序列:
让a和b用最少时间过桥,有两种过桥方案:
方案1: 用最快的成员A传递手电筒,帮助a和b过桥。
方案2: 用最快的成员A和次快的成员B传递手电筒帮助a和b过桥。
代码示例:
#include
#include
int n;
int v[1100];
int main(){
scanf("%d",&n);
for(int i = 1;i <= n;i++) scanf("%d",v+i);
std::sort(v+1,v+1+n);
int p = 1,q = n, ans = 0;
while(q-p+1 > 3){
int A = v[p],B = v[p+1];
int a = v[q],b = v[q-1];
int x = a+b+2*A, y = 2*B+A+a;
if(x > y){
ans += y;
}else{
ans += x;
}
q -= 2;
}
if(q-p+1 == 2) ans += v[2];
else if(q-p+1 == 1) ans += v[1];
else ans += v[1]+v[2]+v[3];
printf("%d\n",ans);
p = 1,q = n,ans = 0;
while(q-p+1 > 3){
int A = v[p],B = v[p+1];
int a = v[q],b = v[q-1];
int x = a+b+2*A, y = 2*B+A+a;
if(x > y){
ans += y;
printf("%d %d\n%d\n%d %d\n%d\n",A,B,A,b,a,B);
}else{
ans += x;
printf("%d %d\n%d\n%d %d\n%d\n",A,a,A,A,b,A);
}
q -= 2;
}
if(q-p+1 == 2) printf("%d %d\n",v[1],v[2]);
else if(q-p+1 == 1) printf("%d\n",v[1]);
else printf("%d %d\n%d\n%d %d\n",v[1],v[2],v[1],v[1],v[3]);
return 0;
}
在一时得不到事物的特征机理的情况下,我们可先通过手算或编程等方法测试得到一些数据,即问题的部分解,再利用数理统计知识对数据进行处理,从而得到最终的数学模型。
解题思路:
最早可能的时间就是假设所有蚂蚁都向靠近它的那一侧走,由于速度相同肯定不会相遇。
最长的时间就是所有蚂蚁都向距离它最远的那一侧走,因为俩只蚂蚁相遇后会回头走,宏观上看就等于俩只蚂蚁穿过了对方继续向前走。
代码示例:
#include
#include
using namespace std;
const int N = 1e6+10;
int a[N],b[N];
int t,n,len;
int main(){
scanf("%d",&t);
while(t--){
int mx = 0;
scanf("%d%d",&len,&n);
for(int i = 1;i <= n;i++) scanf("%d",a+i);
for(int i = 1;i <= n;i++) b[i] = min(a[i],len-a[i]);
for(int i = 1;i <= n;i++) mx = max(b[i],mx);
printf("%d ",mx);
for(int i = 1;i <= n;i++) b[i] = max(a[i],len-a[i]);
for(int i = 1;i <= n;i++) mx = max(b[i],mx);
printf("%d\n",mx);
}
return 0;
}
解题思路:
本题是博弈论中的NIM博弈类型,如果所有堆的火柴数 xor 起来为0,则先手必败,否则先手必胜。
代码示例:
#include
int main(){
int n;
while(~scanf("%d",&n)){
int ans = 0;
for(int i = 1,x;i <= n;i++) scanf("%d",&x),ans ^= x;
if(ans) puts("Yes");
else puts("No");
}
}