简单计算几何+圈环的分析
巡逻机器人https://ac.nowcoder.com/acm/contest/37160/I
给出一个环上的点 机器人在上面走按照顺逆方向开始走 相遇会相反 问走多久可以让时间能走完全程
实际上就是在问每个点距离机器人的最短路程
int32_t main() {
n = read(1) , m = read(1);
for( int i = 1 ; i <= m ; i ++ ) {
p[i] = read(1) , d[i] = read(0);
if( vis[ p[i] ] ) continue;
vis[ p[i] ] = 1 , cnt ++;
}
while( cnt < n ){
tim ++;
for( int i = 1 ; i <= m ; i ++ ){
p[i] += d[i];
if( p[i] == n+1 ) p[i] = 1;
if( p[i] == 0 ) p[i] = n;//直接模拟
if( vis[ p[i] ] ) continue;
vis[ p[i] ] = 1 , cnt ++;
}
}
cout << tim << "\n";
return 0;
}
左边离的最近的一个向右(R)的机器人、在其右边离的最近的一个向左(L)的机器人 这两个机器人中到达该门口时间短的那个时间点。
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
using namespace std ;
using ll = long long ;
using pii = pair<int,int> ;
const int N = 2e6 + 100 ;
int n,m ;
int a[N],b[N] ;
int RTO(int a,int x){ // 计算左边R到达最近时间
if(!a) return 1e9 ;//之前已经特别处理了 还是出现这种情况那就说明确实有问题没有能到达的极其人 设置为无穷大也可以
if(a <= x) return x - a ;
return n - (a - x) ;
}
int LTO(int b,int x){ // 计算右边的L到达最近时间
if(!b) return 1e9 ;
if(b >= x) return b - x ;
return n - (x - b) ;
}
int calc(int a,int b,int x){
return min(RTO(a,x),LTO(b,x)) ;
}
int main(){
scanf("%d%d",&n,&m) ;
for(int i = 1 ; i <= m ; i ++){
char op ;
int x ;
scanf("%d %c",&x,&op) ;
if(op == 'L') b[x] = x ;
else a[x] = x ;
}
// a 记录这个位置最近的的前一个R是那个位置,包含本身,包括环形
for(int i = 1 ; i <= n ; i ++)
a[i] = max(a[i-1],a[i]) ;
for(int i = 1 ; i <= n ; i ++) // 处理环形
if(!a[i]) a[i] = a[n] ;//如果为0 说明左边没有要来这儿的点 只有从y右边饶一圈到左边的也就是a[n]
// b 记录这个位置的后面第一个L是那个位置,包括环形
for(int i = n ; i >= 1 ; i --)
if(!b[i]) b[i] = b[i + 1] ;
for(int i = n ; i >= 1 ; i --)
if(!b[i]) b[i] = b[1] ;
int ans = 0 ;
for(int i = 1 ; i <= n ; i ++) // 对于每个位置来说
ans = max(ans,calc(a[i],b[i],i)) ; // 计算出R到达和L到达的最近
printf("%d\n",ans) ;
return 0 ;
}
链接:https://ac.nowcoder.com/acm/contest/23479/D
来源:牛客网
小红正在雪地中奔跑。雪地可以看成是一个二维平面,小红的初始坐标是 (x_0,y_0)(x 0 ,y 0),她每秒有个方向向量 ( x_i,y_i)(x i,y
i),会沿着该方向直线奔跑1秒(例如,第一秒之后,小红的坐标就变成了 (x_0+x_1,y_0+y_1)(x 0+x 1,y 0+y1)。小果站在坐标 (x,y)(x,y)处原地不动。小红想知道,在跑步过程中,自己和小果的最短距离是多少?
题意找一个人和一个人的最短距离 但是又是跑步过程中 其实就是点到直线的距离即高+两点间的距离(最短距离高所咯在的点不在行进的过程中的时候)
#include<bits/stdc++.h>
using namespace std;
struct point{//点板子
double x,y;
point(double x,double y):x(x),y(y){}
};
double dis(point a,point b){//求距离板子
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
double ar(point A,point B,point C){ //已知三点坐标求三角形面积
double a=dis(B,C),b=dis(A,C),c=dis(A,B);
double p=(a+b+c)/2;
return sqrt(p*(p-a)*(p-b)*(p-c));
}
int jud(point A,point B,point C){ //已经知道三点坐标判断角ABC是钝角
double a=dis(B,C),b=dis(A,C),c=dis(A,B);
return b*b>a*a+c*c;
}
double f(point a,point b,point c){ //c点到ab线段的最小距离
double d1=dis(a,c),d2=dis(b,c);
if(jud(c,a,b)||jud(c,b,a))//如果是钝角三角形 最短距离就是高
return min(d1,d2);
double s=ar(a,b,c);
double d3=2*s/dis(a,b);
return d3;//不是钝角三角,那么最短距离就是高
}
int main(){
int n,i,j,k;
double x,y,x0,y0;
cin>>n;
cin>>x0>>y0>>x>>y;
point purple(x,y);
point red(x0,y0);
double res=1e16;
for(i=0;i<n;i++){
double xt,yt;
cin>>xt>>yt;
point temp(red.x+xt,red.y+yt);
res=min(res,f(red,temp,purple));
red=temp;
}
printf("%.8f",res);
}
https://www.acwing.com/problem/content/1980/ 奶牛过马路
若干个线段高固定 判断几个线段不相交 线段题
排序+rmq 求某个数前面数 后面数的最大值最小值
对i线段不相交 只有当他的ak>ai>aj j是他前面的线段 bj都小于bi k是他后面的线段 只有i后面线段的bk都大于bi 即可
const int N = 100010, INF = 1e8;
int n;
PII q[N];
int smax[N], smin[N];
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i ++ ) scanf("%d%d", &q[i].x, &q[i].y);
sort(q + 1, q + n + 1);
smax[0] = -INF, smin[n + 1] = INF;//求rmq的预处理
for (int i = 1; i <= n; i ++ ) smax[i] = max(smax[i - 1], q[i].y);
for (int i = n; i; i -- ) smin[i] = min(smin[i + 1], q[i].y);
int res = 0;
for (int i = 1; i <= n; i ++ )
if (smax[i - 1] < q[i].y && smin[i + 1] > q[i].y)
res ++ ;
printf("%d\n", res);
return 0;
}
https://www.acwing.com/problem/content/1791/ 牛过路二
看样例以为是 考栈 实际上 考的是枚举
26条线环形判断 交叉线 的组合 求组合 需要i j=i+1 两个指针找组合
判断交叉否 可以判断A 的两个端点 1-x 在是否1-x 之间有且仅有j的一个端点 如果是说明相交
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
vector<int> p[26];
int main()
{
string s;
cin >> s;
for (int i = 0; i < s.size(); i ++ )
p[s[i] - 'A'].push_back(i);
int res = 0;
for (int i = 0; i < 26; i ++ )
for (int j = i + 1; j < 26; j ++ )
{
int cnt = 0;
for (auto y: p[j])
if (p[i][0] < y && y < p[i][1])
cnt ++ ;
if (cnt == 1) res ++ ;
}
cout << res << endl;
return 0;
}
圆形斗篷https://www.acwing.com/problem/content/1845/
我们遍历所有的 nn 个牛棚,以每个牛棚位置为开门位置,分别求出所有奶牛到达位置的总距离,并取所有情况最小值,即为结果。
对于每个起始位置 ii,根据题意,该牛棚的奶牛移动距离为 00,其右侧第 jj 个牛棚(也即编号为第 (i+j)%n(i+j)%n 个牛棚)奶牛移动距离为 jj,以此类推。只需要一次遍历即可。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
int n;
cin >> n;
vector<int> r(n);
for(int i = 0; i < n; ++i) cin >> r[i];
int ret = INT_MAX; // 记录结果最小值,所以初始值设为超级大
for(int i = 0; i < n; ++i) {
// 从第 i 个门进入
int cur = 0;
for(int j = 1; j < n; ++j) {
cur += j * r[(i + j) % n];//j为走了多少步不是房间号 顺时针 i右侧的第j个房间 满了n就()%n
}
ret = min(ret, cur);
}
cout << ret << endl;
return 0;
}
也可以推公式
农田缩减 https://www.acwing.com/problem/content/1828/
给出坐标图内 的点 求减少一个点后 能抱哈这个点的矩形的最小面积
即求 少了一个点后 所有点的xmax xmin ymax ymin,
优化如果这个点本身就不是xmax和xmin之间的点 那么就不用考虑
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N],b[N],x[N],y[N];
int main(){
int n;cin>>n;
for(int i=0;i<=n;i++){
cin>>x[i]>>y[i];
a[i]=x[i];
b[i]=y[i];
}
sort(a,a+n);/排序logn
sort(b,b+n);
int res=INT_MAX;
for(int i=0;i<n;i++){
int xmin=x[i]==a[0]?a[1]:a[0];
int xmax=x[i]==a[n-1]?a[n-2]:a[n-1];
int ymin=y[i]==b[0]?b[1]:b[0];
int ymax=y[i]==b[n-1]?b[n-2]:b[n-1];
res=min(res,(ymax-ymin)*(xmax-xmin));
}
cout<<res;
}
闪烁https://www.acwing.com/problem/content/1962/
n盏灯每栈灯如果上一时刻前一盏灯是1那么这盏灯就会切换状态
因为n盏灯只有开和关两种状态 所以共有 1<<n 种状态
所以 状态压缩->将状态存二进制数 然后用10进制来存
1^15那么秒次操作 就需要进行位运算
剩下的就是位运算
t=k>>n&1 表示获得k二进制n位上的数字
|或运算 可以(0 1)位最大值 如果是|0的话就不变 强制给最后一位赋值为1
n|1-1变成最小的偶数 n|1变成最小的奇数
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 1 << 16;//总的状态大小,以这个状态开空间不会爆
int n;
LL m;
int p[N];//p的值表示这个状态是第几部走到的
int update(int state)//更新状态
{
int res = 0;
for (int i = 0; i < n; i ++ )
{
int j = (i - 1 + n) % n;//取得二进制第i位下左边一位
int x = state >> i & 1, y = state >> j & 1;//取得最后一位
res |= (x ^ y) << i;//对于新的第i位等于两者的异或
}
return res;
}
void print(int state)
{
for (int i = 0; i < n; i ++ )
cout << (state >> i & 1) << endl;//按位输出每个状态
}
int main()
{
cin >> n >> m;
int state = 0;//用int的state 表示总的状态
for (int i = 0; i < n; i ++ )
{
int x;
cin >> x;
state |= x << i;//每个等的初始状态 x 加到总状态state上
}
memset(p, -1, sizeof p);//初始话
p[state] = 0;
for (int i = 1;; i ++ )
{
state = update(state);//更新一次状态
if (i == m)//如果还没进入了循环就到达了最终的状态就打印
{
print(state);
break;
}
else if (p[state] == -1) p[state] = i;//如果这个状态是第一次到达的就给这个状态记录上是第几个状态到达的
else
{
int len = i - p[state];//环的长度等于上一次到达这里和本次到达的差
int r = (m - i) % len;//还需要走多少次 这里i也可以是p【state】
while (r -- ) state = update(state);//再走这么多步就可以了
print(state);
break;
}
}
return 0;
}
三角形https://www.acwing.com/activity/content/problem/content/6741/
计算三角形 o n3
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define x first
#define y second
#define PII pair<int,int>
const int N = 105;
PII q[N];
// vector<PII>y[N];
// unordered_map<int,int>y;
int main()
{
int n;cin>>n;
for (int i = 0; i < n; i ++ ){
// int a,b;
cin >> q[i].x>>q[i].y;
// pos.push_back({a,b});
// y[b]=a;
}
// sort(.begin(),x.end());
// sort(y.begin(),y.end());
int res=0;
for (int i =0; i <n; i++ ){
for (int j = 0; j < n; j ++ ){
for (int k = 0; k < n; k ++ ){
if(i!=j&&j!=k&&i!=k){//on3代替全排序
if(q[i].x==q[j].x&&q[i].y==q[k].y){//三个点满足横坐标等,纵坐标等
res=max(res,abs(q[i].y-q[j].y)*abs(q[j].x-q[k].x));
}
}
}
}
}
cout << res;
return 0;
}