2019矿大icpc夏令营Day4-求解Ad Hoc类问题

求解AD Hoc类问题的编程实验

(拉丁语)Ad Hoc:为某种目的而特别设计的。

在程序设计竞赛的试题中,有这样一类试题,解题不能套用现成的算法,也没有模式化的求解方法,而是需要编程者自己设计算法来解答试题,这类试题被称作Ad Hoc类试题,也被称为杂题。实现的程序一般比较简短。
求解Ad Hoc类问题的两类方法:

  1. 机理分析法:采用顺向思维,从分析机理内部出发顺推算法。
  2. 统计分析法:采用逆向思维,从分析部分解出发倒推算法。

机理分析法

机理分析法,就是根据客观事物的特性,分析其内部的机理,弄清其内在的关系,在适当抽象的条件下,得到可以描述事物属性的数学工具。

POJ2661 Factstone Benchmark

解题思路:
首先读懂题意,并总结归纳出解决本题的数学方法,可以得出“本题是求一个n,使得 n ! < = 2 k − 1 n! <= 2^k-1 n!<=2k1”,这里的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;
}
POJ2573 Bridge

解题思路:
经过分析,如果想让最慢的两个人过桥(让且只让最慢的两个人过桥,其他人位置不变),显然有两种合理的解决方案;这两种方案在两种不同情况下都是正确的,但是若依据速度的值来分类讨论就太过于复杂了,因此我们可以每次比较两种方案“将最慢的两个人送过桥”,选取二者中较优的一种,可以证明这样的选取策略一定能得到最优解。
当然,这种策略最终会遇到的情况是:还剩2个人或3个人未过桥(剩余人数 < 4),此时特殊处理一下即可。

设当前序列:

  • A是最快的人,B是次快的人,A和B是序列首部的两个元素;
  • a是最慢的人,b是次慢的人,a和b是序列尾部的两个元素;

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

统计分析法

在一时得不到事物的特征机理的情况下,我们可先通过手算或编程等方法测试得到一些数据,即问题的部分解,再利用数理统计知识对数据进行处理,从而得到最终的数学模型。

POJ1852 Ants

解题思路:
最早可能的时间就是假设所有蚂蚁都向靠近它的那一侧走,由于速度相同肯定不会相遇。
最长的时间就是所有蚂蚁都向距离它最远的那一侧走,因为俩只蚂蚁相遇后会回头走,宏观上看就等于俩只蚂蚁穿过了对方继续向前走。

代码示例:

#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;
} 
POJ2234 Matches Game

解题思路:
本题是博弈论中的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");
	}
}

你可能感兴趣的:(假期练习)