cogs2081Asm_def的模拟赛解题报告

【题目描述】


Asm.Def经过复杂的计算后,切断了合适的线路,使得透明网络计算离线,整个世界开始恢复正常了。但是有些旧的秩序因为不受透明计算网络的控制,变得更加混乱了,比如说NOIP变成了NOI Professional,NOI变成了National Olympiad in Data Structure。为了整治乱象,使得世界秩序恢复正常,Asm.Def决定出一套NOIP模拟赛,让大家看看真的的NOIP难度,重建OI秩序。

Asm.Def出的题如下:现在平面上有n个点,要求你选择其中3个不同点,使得这3个点组成的三角形覆盖平面上的点最多。

可是Asm.Def发现这道NOIP题并不是这么容易,所以为了世界和平,解答这道题的任务交给你了。


【输入格式】


第一行一个整数n,表示平面上有n个点。

接下来n行,每行两个正整数x, y表示在x, y有一个点。

保证所有点不重合

保证不存在三点共线


【输出格式】


输出两行,第一行为选择的三角形最多可以覆盖几个点。

第二行为有多少选择方案使得覆盖的最多。

我们认为存在一个点为第一种方案的三角形某个顶点却不为第二种方案的三角形某个顶点时,这两种方案不同。


【样例输入】

5
2 1
3 3
-1 -1
2 -1
-1 1

【样例输出】

4
2
题解:
预处理出i与j所成线段下方的点(不包括左右端点) 和第i个点正下方有多少个点 那么我们在枚举组成三角形的三点就可以o(1)算出三角形中包括的点数
判断三角形是^型还是v型可以用中间点在其他两点解析式上的位置计算 预处理时不处理左右端点可以方便计算(+3再特判需不需要减去\加上中间点的down)
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
struct T{
	double x,y;	
}A[310];
int num[310][310];
int down[310];
int u,orz,otz;
inline int get_toward(int i,int j,int k){
	double  maxx=-0x7fffffff,minn=0x7fffffff;
	if(A[i].x<=max(A[j].x,A[k].x)&&A[i].x>=min(A[j].x,A[k].x)){
		u=i;
		orz=j,otz=k;
	}
	else if(A[j].x<=max(A[i].x,A[k].x)&&A[j].x>=min(A[i].x,A[k].x)){
		u=j;
		orz=i,otz=k;
	}
	else {
		u=k;
		orz=j,otz=i;
	}
	double K=(A[orz].y-A[otz].y)/(A[orz].x-A[otz].x);
	double B=A[orz].y-A[orz].x*K;
	double yy=K*A[u].x+B;
	if(yy>A[u].y)
		return 2;
	return 1;
}
int main(){
	freopen("trib.in","r",stdin);
	freopen("trib.out","w",stdout);
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%lf %lf",&A[i].x,&A[i].y);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(i==j)
				continue;
			if(A[j].x==A[i].x&&A[j].y<A[i].y)
				down[i]++;
			
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=i+1;j<=n;j++){
			if(A[i].x==A[j].x){
				num[i][j]=num[j][i]=0;
				continue;
			}
			double k=(A[i].y-A[j].y)/(A[i].x-A[j].x);
			double b=A[i].y-A[i].x*k;
			for(int u=1;u<=n;u++){
				if(u==i||u==j)
					continue;
				double mx=max(A[i].x,A[j].x);
				double mi=min(A[i].x,A[j].x);
				if(A[u].x>=mx||A[u].x<=mi)
					continue;
				double yy=k*A[u].x+b;
				if(yy>A[u].y)
					num[i][j]++;
			}
			num[j][i]=num[i][j];
		}
	}
	int maxx=-0x7fffffff;
	int ans=0;
	for(int i=1;i<=n;i++)
		for(int j=i+1;j<=n;j++)
			for(int k=j+1;k<=n;k++){
				int tmp=get_toward(i,j,k);
				int sum=0;
				if(tmp==1){
					sum+=num[orz][u];
					sum+=num[otz][u];
					sum-=num[orz][otz];
					sum+=down[u];
					if(A[u].x==A[orz].x||A[u].x==A[otz].x)
						sum--;
					sum+=3;
					if(sum>maxx){
						maxx=sum;
						ans=1;
					}
					else if(sum==maxx)
						ans++;
				}
				else {
					sum+=num[orz][otz];
					sum-=num[u][orz];
					sum-=num[u][otz];
					sum-=down[u];
					if(A[u].x!=A[orz].x&&A[u].x!=A[otz].x)
						sum--;
					sum+=3;
					if(sum>maxx){
						maxx=sum;
						ans=1;
					}
					else if(sum==maxx)
						ans++;
				}
				//printf("%.0lf %.0lf %.0lf %.0lf %.0lf %.0lf\n",A[i].x,A[i].y,A[j].x,A[j].y,A[k].x,A[k].y);
				//printf("%d\n",tmp);
				//printf("%d\n",sum);
			}
	printf("%d\n%d\n",maxx,ans);
return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值