大一下第八周学习笔记

 

最近作业和acm有点平衡不好

每天至少抽一个小时训练吧

作业也要认真写,有条不紊地完成任务

周二 4.20 (训练赛补题)

昨天写作业去了,作业好多

D. Digits(dp + 方案输出 + log防止溢出)

今天3个多小时就补了这道题,因为一个很隐晦的bug调试了很久很久

这道题还是学到蛮多东西的,学到很多,自己不会的地方还有很多

比赛时候做这道题我是有想到dp的,但是没有想到怎么设置状态

其实是因为我太久没做dp的缘故。我只想到了i为第1维表示前i位

但显然还要有一维维护其他信息,也就是多一维j表示以j为结尾

所以dp[i][j]表示前i位中以j为结尾的乘积最大值

方程就是dp[i][j] = max(dp[i-1][j], dp[i-1][k] * a[i]) ((k * j) % 10 = i)

分别表示第i位取和不取的两种情况

初始化是dp[i][a[i] % 10] = a[i]

循环的时候顺便初始化就好

 

但这道题没这么简单,还有两个难点要处理

(1)相乘后数会非常大,long double都存不下

这时有个技巧,就是取log。取log之后就变的很小的,库函数中log表示以e位底,log2表示以2为底

所以写的时候就要化乘法为加法

取log的时候还要注意一点

就是转移的时候dp[i-1][k]要存在才行,不能从一个不合法的状态转移过来

如果没取log,为0的时候直接相乘为0,顺便就忽略掉了

但是取了log之后,就不会自动忽略掉了

或者说就默认e的0次方为1了,如果取log话就要全部化为log0 = 负无穷,全部化-1e9

当然实际操作可以直接判断一下dp[i-1][k]不为0

(2)输出方案

dp输出方案是我的知识盲区,其实不难

开一个结构体记录一下当前是从哪个状态转移过来的就行

然后最后从结果逆推就好

这里我遇到了一个超级隐晦的错误,调了2个小时最后才发现

深刻牢记!!

#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++) 
#define _for(i, a, b) for(int i = (a); i <= (b); i++) 
using namespace std;
 
const int N = 1e5 + 10;
double dp[N][10];
int a[N], n, d;
vector<int> ans;
 
struct node //记录方案数。表示当前是由dp[x][y]转移过来的。use表示当前这个状态有没有用a数组 
{
	bool use;
	int x, y;
}pre[N][10];
 
int main()
{
	scanf("%d%d", &n, &d);
	_for(i, 1, n) scanf("%d", &a[i]);
	
	_for(i, 1, n)
	{
		dp[i][a[i] % 10] = log(a[i]); //初始化 
		pre[i][a[i] % 10] = {true, 0, 0}; //起点的pre数组特殊一点,没有从哪里转过来,它自己就是起点。用x = 0表示 y随意 
		
		_for(j, 0, 9) 
			if(dp[i][j] < dp[i - 1][j]) //当前这一位不取 
			{
				dp[i][j] = dp[i - 1][j];  
				pre[i][j] = {false, i - 1, j};
			}
		
		_for(j, 0, 9) //当前这一位取,枚举前一位末尾 
		{
			int now = (a[i] * j) % 10;
			if(dp[i - 1][j] && dp[i][now] < dp[i - 1][j] + log(a[i])) //取log防止溢出 
			{                                                         //注意要dp[i-1][j]存在才行。这里取了log要特殊一些,以往要是为0相乘后也为0 
				dp[i][now] = dp[i - 1][j] + log(a[i]);
				pre[i][now] = {true, i - 1, j};
			}
		}
	}
	
	if(!dp[n][d]) return puts("-1") && 0; //骚操作 
	
	int x = n, y = d;
	while(1)
	{
		if(pre[x][y].use) ans.push_back(a[x]);
		int t = x;   //超级隐晦的错误!!下面那一行y的转移不能直接写x 
		x = pre[x][y].x, y = pre[t][y].y;
		if(!x) break; 
	}
		
	printf("%d\n", ans.size());
	for(auto x: ans) printf("%d ", x); puts("");
	
	return 0;
}

周三 4.21 (计算几何)

最近开始搞计算几何

先弄一些基础内容和凸包

另外我想做一个dp选手,暑假可以疯狂练习各种dp题目

计算几何模板

#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)  
using namespace std;

const double eps = 1e-8;
const double pi = acos(-1.0); 

struct point //点或向量 
{
	double x, y;
	point(double x = 0, double y = 0) : x(x), y(y) {}
};

int dcmp(double x) //由于精度问题,此函数用来判断大于0 小于0 等于0 
{
	if(fabs(x) < eps) return 0;
	if(x > 0) return 1;
	return -1;	
} 

point operator + (point a, point b) { return {a.x + b.x, a.y + b.y}; }
point operator - (point a, point b) { return {a.x - b.x, a.y - b.y}; }
point operator * (point a, double k) { return {a.x * k, a.y * k}; }    //数乘 
point operator / (point a, double k) { return {a.x / k, a.y / k}; }    //数除 
double operator * (point a, point b) { return a.x * b.x + a.y * b.y; } //点乘 
double operator ^ (point a, point b) { return a.x * b.y - a.y * b.x; } //叉乘 
point operator == (point a, point b) { return dcmp(a.x - b.x) == 0 && dcmp(a.y - b.y) == 0; }

double length(point a) { return sqrt(pow(a.x, 2) + pow(a.y, 2)); } //向量的模
double dis(point a, point b) { return length(a - b); }             //两点间的距离 
double angle(point a, point b) { acos(a * b / length(a) / length(b)); } //夹角,弧度制 
double area(point a, point b, point c) { return fabs((a - b) ^ (c - b) / 2); } //三角形面积 注意绝对值 
point rotate(point a, double rad) { return {a.x * cos(rad) - a.y * sin(rad), a.x * sin(rad) + a.y * cos(rad)}; } //逆时针旋转 

int main()
{
	
	
	return 0;
}

周四 4.22 (凸包)

最近事情比较多,作业也比较多,所以训练得比较少

P2742【模板】二维凸包

这道题WA了好久,好好总结一下

凸包的算法就是先找一个起始点,找到y最小的点,y同找x最小的点

然后把其他的点按照极角排序,极角相同就按照距离

然后一个点一个点遍历,用栈维护

我可以发现凸包一定是向左转的,所以我们可以判断一下新来的点是向左转还是向右转,用叉积正负来判断

如果不符合就去掉栈顶的点,直到符合就加入栈

最后栈中的元素就是凸包上的点

这里有一些细节要注意,主要是精度问题

(1)因为有误差,所以一些比较,判断大小相等的要注意用dcmp函数,尤其是相等的,还有一些可能相等的

(2)eps一般设置1e-8

#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;

const double eps = 1e-8; //一般都设置1e-8 我设置了1e-4 WA了一半的点 
const int N = 1e5 + 10;

struct point
{
	double x, y;
	point(double x = 0, double y = 0) : x(x), y(y) {}
}p[N], st[N];
int top, n;

point operator - (point a, point b) { return {a.x - b.x, a.y - b.y}; }
double operator ^ (point a, point b) { return a.x * b.y - a.y * b.x; }
double dis(point a, point b) { return sqrt(pow(a.x - b.x, 2) + pow(a.y - b.y, 2)); }

int dcmp(double x) //精度问题。相等时一定要用 
{
	if(fabs(x) < eps) return 0;
	if(x > 0) return 1;
	return -1;
}

//cmp函数法一 
bool cmp(point a, point b) //atan2(y, x) 表示(x, y)与原点连线的极角,注意是原点   
{
	int t = dcmp(atan2(a.y - p[1].y, a.x - p[1].x) - atan2(b.y - p[1].y, b.x - p[1].x));
	if(t != 0) return t < 0;
	return dis(a, p[1]) < dis(b, p[1]); //极角同看距离 
}

/* cmp函数法二 
bool cmp(point a, point b) //atan2(y, x) 表示(x, y)与原点连线的极角,注意是原点  另一种方法 
{
	double ans = (a - p[1]) ^ (b - p[1]);
	if(dcmp(ans) == 0) return dis(a, p[1]) < dis(b, p[1]);
	return ans > 0;
}*/

int main()
{
	scanf("%d", &n);
	_for(i, 1, n) 
	{
		scanf("%lf%lf", &p[i].x, &p[i].y);
		if(p[i].y < p[1].y || dcmp(p[i].y - p[1].y) == 0 && p[i].x < p[1].x) swap(p[1], p[i]); //等于0一定要用dcmp 
	}                                                                        //y相同选x小的 
	sort(p + 2, p + n + 1, cmp);
	
	st[++top] = p[1];
	st[++top] = p[2];
	_for(i, 3, n)
	{
		while(top >= 2 && dcmp((st[top] - st[top - 1]) ^ (p[i] - st[top - 1])) <= 0) top--; //这个向量不要写错了 
		st[++top] = p[i];
	}
	
	st[++top] = p[1]; //加上起点 
	double ans = 0;
	_for(i, 1, top - 1)
		ans += dis(st[i], st[i + 1]);
	printf("%.2lf\n", ans);
	
	return 0;
}

周五 4.23(凸包)

学了凸包了另一种算法,我发现我更喜欢另一种算法

思路就是先排序,找到最坐标的点,这个点一定是凸包上最左边的点

然后我们逆时针求凸包,一定要先左转,如果向右转就弹出栈中的点

所以先求下半凸包,再求上半凸包就行了

P2742【模板】二维凸包

#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++) 
#define _for(i, a, b) for(int i = (a); i <= (b); i++) 
using namespace std;

const double eps = 1e-8;
const int N = 1e5 + 10;

struct point
{
	double x, y;
	point(double x = 0, double y = 0) : x(x), y(y) {}
}st[N], p[N];
int n, top;

point operator - (point a, point b) { return {a.x - b.x, a.y - b.y}; }
double operator ^ (point a, point b) { return a.x * b.y - a.y * b.x; } //叉积数学上返回向量,程序这里返回的是值 
double dis(point a, point b) { return sqrt(pow(a.x - b.x, 2) + pow(a.y - b.y, 2)); }

bool cmp(point a, point b) //横坐标第一关键字,纵坐标第二关键字 
{
	if(fabs(a.x - b.x) < eps) 
		return a.y < b.y;
	return a.x < b.x;
}

int main()
{
	scanf("%d", &n);
	_for(i, 1, n) scanf("%lf%lf", &p[i].x, &p[i].y);
	sort(p + 1, p + n + 1, cmp);
	
	st[++top] = p[1]; //先加入第一个点,第一个点一定是凸包上的点 
	_for(i, 2, n)  //求下半凸包 
	{
		while(top >= 2 && ((st[top] - st[top - 1]) ^ (p[i] - st[top - 1])) < 0) top--; //这里有一个一条边上多个点的情况,也就是相等的情况 
		st[++top] = p[i];                                                              //但是这道题不影响结果 
	}                                                                                  //如果要去掉的话就改成<=0 还要写eps 
	for(int i = n - 1; i >= 1; i--) //求上半凸包,直接复制,改一下循环顺序就好 
	{
		while(top >= 2 && ((st[top] - st[top - 1]) ^ (p[i] - st[top - 1])) < 0) top--; 
		st[++top] = p[i];
	}
	
	double ans = 0;
	_for(i, 1, top - 1) //最后栈里面首尾都是起点,所以可以直接求距离 
		ans += dis(st[i], st[i + 1]);
	printf("%.2lf\n", ans);
	
	return 0;
}

P3829 [SHOI2012]信用卡凸包

这题思路挺好想的,把边移动一下,就可以发现答案就是凸包周长加上2pir

但是我卡在旋转上,我的计算几何的基础的一些操作还不怎么熟练

用上模板的一个旋转操作,先旋转再加上中心

那个模板是连上原点来旋转的

#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++) 
#define _for(i, a, b) for(int i = (a); i <= (b); i++) 
using namespace std;

const double eps = 1e-8;
const double pi = acos(-1.0);
const int N = 4e4 + 10;

struct point
{
	double x, y;
	point(double x = 0, double y = 0) : x(x), y(y) {}
}st[N];
vector<point> ve;
int n, top;
double a, b, r;

point operator + (point a, point b) { return {a.x + b.x, a.y + b.y}; }
point operator - (point a, point b) { return {a.x - b.x, a.y - b.y}; }
double operator ^ (point a, point b) { return a.x * b.y - a.y * b.x; } 
point rotate(point a, double rad) { return {a.x * cos(rad) - a.y * sin(rad), a.x * sin(rad) + a.y * cos(rad)}; } //逆时针旋转 
double dis(point a, point b) { return sqrt(pow(a.x - b.x, 2) + pow(a.y - b.y, 2)); } 

bool cmp(point a, point b)
{
	if(fabs(a.x - b.x) < eps) 
		return a.y < b.y;
	return a.x < b.x;
}

int main()
{
	scanf("%d%lf%lf%lf", &n, &a, &b, &r);
	a /= 2; b /= 2;
	_for(i, 1, n)
	{
		double x, y, c;
		scanf("%lf%lf%lf", &x, &y, &c);
		ve.push_back(rotate({b - r, a - r}, c) + point(x, y));
		ve.push_back(rotate({b - r, r - a}, c) + point(x, y));
		ve.push_back(rotate({r - b, a - r}, c) + point(x, y));
		ve.push_back(rotate({r - b, r - a}, c) + point(x, y));
	}
	
	sort(ve.begin(), ve.end(), cmp);
	REP(i, 0, ve.size())
	{
		while(top >= 2 && ((st[top] - st[top - 1]) ^ (ve[i] - st[top - 1])) < 0) top--;
		st[++top] = ve[i];
	}
	for(int i = ve.size() - 2; i >= 0; i--)
	{
		while(top >= 2 && ((st[top] - st[top - 1]) ^ (ve[i] - st[top - 1])) < 0) top--;
		st[++top] = ve[i];
	}
	
	double ans = 0;
	_for(i, 1, top - 1)
		ans += dis(st[i], st[i + 1]);
	printf("%.2lf\n", ans + 2 * pi * r);

	return 0;
}

先打牢基础吧

先打kuangbin计算几何基础的题单

之后再做各种凸包的题目

 

poj 2318(叉积判断方向)

这题用到叉积判断方向

也就是用叉积判断一个点在一个向量的左边还是右边

方法是多构造一个向量然后叉积,判断叉积的正负

我看到n 5000直接n方暴力

注意这里我自己作死

我判断异号的时候用的是相乘小于0

然后爆int了

改成一个小于0一个大于0就AC了

#include<iostream>
#include<cstring>
#define REP(i, a, b) for(int i = (a); i < (b); i++) 
#define _for(i, a, b) for(int i = (a); i <= (b); i++) 
using namespace std;

const int N = 5e3 + 10;
int ans[N], n, m, x1, y1, x2, y2;

struct point
{
	int x, y;
	point(int x = 0, int y = 0) : x(x), y(y) {} 
}up[N], down[N];

point operator - (point a, point b) { return point(a.x - b.x, a.y - b.y); }
int operator ^ (point a, point b) { return a.x * b.y - a.y * b.x; }

int main()
{
	while(scanf("%d", &n) && n)
	{
		memset(ans, 0, sizeof(ans));
		scanf("%d%d%d%d%d", &m, &x1, &y1, &x2, &y2);
		_for(i, 1, n) 
		{
			scanf("%d%d", &up[i].x, &down[i].x);
			up[i].y = y1; down[i].y = y2; 
		}
		up[0] = point(x1, y1); down[0] = point(x1, y2); 
		up[n + 1] = point(x2, y1); down[n + 1] = point(x2, y2); 
		
		_for(i, 1, m)
		{
			int x, y;
			scanf("%d%d", &x, &y);
			_for(i, 0, n)
			{
				int t1 = (up[i] - down[i]) ^ (point(x, y) - down[i]);
				int t2 = (up[i + 1] - down[i + 1]) ^ (point(x, y) - down[i + 1]);
				if(t1 < 0 && t2 > 0)
				{
					ans[i]++;
					break;
				}
			}
		}
		
		_for(i, 0, n) printf("%d: %d\n", i, ans[i]); puts("");
	}

	return 0;
}

当然可以不暴力,很显然可以二分

看作0000100000去二分就好

#include<iostream>
#include<cstring>
#define REP(i, a, b) for(int i = (a); i < (b); i++) 
#define _for(i, a, b) for(int i = (a); i <= (b); i++) 
using namespace std;

const int N = 5e3 + 10;
int ans[N], n, m, x1, y1, x2, y2;

struct point
{
	int x, y;
	point(int x = 0, int y = 0) : x(x), y(y) {} 
}up[N], down[N];

point operator - (point a, point b) { return point(a.x - b.x, a.y - b.y); }
int operator ^ (point a, point b) { return a.x * b.y - a.y * b.x; }

int check(int i, int x, int y)
{
	int t1 = (up[i] - down[i]) ^ (point(x, y) - down[i]);
	int t2 = (up[i + 1] - down[i + 1]) ^ (point(x, y) - down[i + 1]);
	if(t1 < 0 && t2 < 0) return -1;
	if(t1 > 0 && t2 > 0) return 1;
	return 0;
}

int main()
{
	while(scanf("%d", &n) && n)
	{
		memset(ans, 0, sizeof(ans));
		scanf("%d%d%d%d%d", &m, &x1, &y1, &x2, &y2);
		_for(i, 1, n) 
		{
			scanf("%d%d", &up[i].x, &down[i].x);
			up[i].y = y1; down[i].y = y2; 
		}
		up[0] = point(x1, y1); down[0] = point(x1, y2); 
		up[n + 1] = point(x2, y1); down[n + 1] = point(x2, y2); 
		
		_for(i, 1, m)
		{
			int x, y;
			scanf("%d%d", &x, &y);
			
			int l = -1, r = n + 1;
			while(1)
			{
				int mid = l + r >> 1;
				int t = check(mid, x, y);
				if(t > 0) r = mid;
				else if(t < 0) l = mid;
				else { ans[mid]++; break; }
			}
		}
		
		_for(i, 0, n) printf("%d: %d\n", i, ans[i]); puts("");
	}

	return 0;
}

周六 4.24(计算几何基础)

poj 2398

和昨天那题差不多

统计答案的方式改变一下

然后就是多构造一个line结构体,结构体排序一下

#include<iostream>
#include<cstring>
#include<algorithm>
#define REP(i, a, b) for(int i = (a); i < (b); i++) 
#define _for(i, a, b) for(int i = (a); i <= (b); i++) 
using namespace std;
 
const int N = 1e3 + 10;
int ans[N], cnt[N], n, m, x1, y1, x2, y2;
 
struct point
{
	int x, y;
	point(int x = 0, int y = 0) : x(x), y(y) {} 
};

struct line
{
	point up, down;
	line(point up = 0, point down = 0) : up(up), down(down) {} 
}l[N];
 
point operator - (point a, point b) { return point(a.x - b.x, a.y - b.y); }
int operator ^ (point a, point b) { return a.x * b.y - a.y * b.x; }
 
int check(int i, int x, int y)
{
	int t1 = (l[i].up - l[i].down) ^ (point(x, y) - l[i].down);
	int t2 = (l[i + 1].up - l[i + 1].down) ^ (point(x, y) - l[i + 1].down);
	if(t1 < 0 && t2 < 0) return -1;
	if(t1 > 0 && t2 > 0) return 1;
	return 0;
}

bool cmp(line a, line b)
{
	return a.up.x < b.up.x;
}
 
int main()
{
	while(scanf("%d", &n) && n)
	{
		memset(ans, 0, sizeof(ans));
		memset(cnt, 0, sizeof(cnt));
		
		scanf("%d%d%d%d%d", &m, &x1, &y1, &x2, &y2);
		_for(i, 1, n) 
		{
			scanf("%d%d", &l[i].up.x, &l[i].down.x);
			l[i].up.y = y1; l[i].down.y = y2; 
		}
		l[0].up = point(x1, y1); l[0].down = point(x1, y2); 
		l[n + 1].up = point(x2, y1); l[n + 1].down = point(x2, y2);
		sort(l, l + n + 1, cmp); 
		
		_for(i, 1, m)
		{
			int x, y;
			scanf("%d%d", &x, &y);
			
			int l = -1, r = n + 1;
			while(1)
			{
				int mid = l + r >> 1;
				int t = check(mid, x, y);
				if(t > 0) r = mid;
				else if(t < 0) l = mid;
				else { ans[mid]++; break; }
			}
		}
		
		_for(i, 0, n) cnt[ans[i]]++;
		puts("Box");
		_for(i, 1, n)
			if(cnt[i])
				printf("%d: %d\n", i, cnt[i]);
	}
 
	return 0;
}

poj 3304(判断直线和线段是否相交)

这道题我思路方向错了

我发现投影的话,只和直线的斜率有关

所以我就枚举两个线段,判断要使得这两个线段投影区域有重合时斜率的范围

然后所有的结果如果存在共同的区间就是有解的

结果我发现实现起来非常的复杂,我计算几何基础不牢,写了两个小时也没有写出个正确的方法

后来看了题解,发现正解是逆推,把结论变成一个等价命题,挺秀的

如果满足条件,把那一点做直线的垂线,这条直线就和所有线段相交

然后我们再满足与所有线段相交的情况下左右旋转直线,在极限位置这个直线是和两个线段端点相交

所以就转化为这个充要条件,等价命题

枚举所有不同线段的端点连成的直线,判断是否存在一条直线与所有线段相交

这里我弄错了一点,我之前写过线段与线段相交的代码

但这个是线段与直线相交,是不一样的

注意包括了端点就要写等号

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#define REP(i, a, b) for(int i = (a); i < (b); i++) 
#define _for(i, a, b) for(int i = (a); i <= (b); i++) 
using namespace std;

const int N = 1e2 + 10;
const double eps = 1e-8;

struct point
{
	double x, y;
	point(double x = 0, double y = 0) : x(x), y(y) {}
};

struct line
{
	point a, b;
	line(point a = 0, point b = 0) : a(a), b(b) {}
}L[N];

int n, ans;

point operator - (point a, point b) { return point(a.x - b.x, a.y - b.y); }
double operator ^ (point a, point b) { return a.x * b.y - a.y * b.x; }

int dcmp(double x)
{
	if(fabs(x) < eps) return 0;
	if(x > 0) return 1;
	return -1;
}

double dis(point a, point b) { return sqrt(pow(a.x - b.x, 2) + pow(a.y - b.y, 2)); }

bool judge(point a, point b)
{
	if(dcmp(dis(a, b)) == 0) return false;
	_for(i, 1, n)
		if(((a - L[i].a) ^ (b - L[i].a)) * ((a - L[i].b) ^ (b - L[i].b)) > 0)
			return false;
	return true;
}

int main()
{
	int T; scanf("%d", &T);
	while(T--)
	{
		ans = 0; 
		scanf("%d", &n);
		_for(i, 1, n) scanf("%lf%lf%lf%lf", &L[i].a.x, &L[i].a.y, &L[i].b.x, &L[i].b.y);
		if(n == 1) { puts("Yes!"); continue; }
		
		_for(i, 1, n)
		{
			_for(j, i + 1, n)
			{
				if(judge(L[i].a, L[j].a) || judge(L[i].a, L[j].b) ||
				judge(L[i].b, L[j].a) || judge(L[i].b, L[j].b)) 
				{
					ans = 1;
					break;
				}
			}
			if(ans) break;
		}
		
		puts(ans ? "Yes!" : "No!");
	}
 
	return 0;
}

周日 4.25(训练赛补题)

这次状态不错,很快就做了前三题,都是第一个AC的

然后去做难一些的题目时,题意理解错了

直接导致了我没做出来

有点可惜,但是没关系,一次训练而已。

poj 2074(思维)

英文题,考试时我直接翻译的,然后有一句非常关键的话,翻译过来很奇怪

这个时候我应该看英文到底在讲什么的,但是我没有,我直接按照我的理解写了,直接导致没AC,其实不难

以后训练可以用翻译,但是那些不是很清楚的地方就要看英文原文。

可以用有中英文的翻译软件,或者开两个网页这样

 

这道题就是对每个区间求被遮盖的区间

然后把有重叠部分的区间合并

然后求一些区间与区间之间的部分就好

中间要手推一下公式,注意一下细节

注意用vector时前提是有元素,这就提醒我自己存在没有元素的情况,要特判一下

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<vector>
#define l first
#define r second
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)  
using namespace std;

const int N = 1e5 + 10;
double X1[N], X2[N], y[N];
double x1, x2, y1, x3, x4, y2;
int n;

vector<pair<double, double> > segment, ve;

double cal(double x1, double y1, double x1i, double yi)
{
	return (y2 - y1) * (x1i - x1) / (yi - y1) + x1;
}

int main()
{
	while(scanf("%lf%lf%lf", &x1, &x2, &y1))
	{
		segment.clear(); ve.clear();
		if(x1 == 0 && x2 == 0 && y1 == 0) break;
		scanf("%lf%lf%lf%d", &x3, &x4, &y2, &n);
		_for(i, 1, n) scanf("%lf%lf%lf", &X1[i], &X2[i], &y[i]);

		_for(i, 1, n)
		{
			if(y[i] >= y1 || y[i] <= y2) continue;
			double r = cal(x1, y1, X2[i], y[i]);
			double l = cal(x2, y1, X1[i], y[i]);
			l = max(l, x3); r = min(r, x4);
			if(r < l) continue;
			segment.push_back(make_pair(l, r));
		}
		
		if(segment.empty())
		{
			printf("%.2f\n", x4 - x3);
			continue;
		}
		
		sort(segment.begin(), segment.end());
		double l = segment[0].l, r = segment[0].r;
		REP(i, 1, segment.size())
		{
			if(segment[i].l <= r) r = max(r, segment[i].r);
			else 
			{
				ve.push_back(make_pair(l, r));
				l = segment[i].l, r = segment[i].r;
			}
		}
		ve.push_back(make_pair(l, r));
		
		double ans = max(ve[0].l - x3, x4 - ve[ve.size() - 1].r);
		REP(i, 0, ve.size() - 1) ans = max(ans, ve[i + 1].l - ve[i].r);
		
		if(fabs(ans) < 1e-8) puts("No View");
		else printf("%.2f\n", ans);
	}

	return 0;
}

C. Subsequences(树状数组优化dp)

这道题考试时根本没看

比赛完做了一下发现挺顺利地做了出来

这又是我的失误了

比赛后两个小时花在一道看错的题的题上

其实我应该理解完所有题目的题意的,稍微10分钟想想

没事,一场训练赛而已,吸取教训

一个是不要看错题,一个是不要死磕一道题而漏掉了那些可以做出来的题目

 

这道题dp方程很好想,关键是怎么优化

发现每次转移都是j-1的前提下所有小于a[i]的dp值的和

所以我们可以在每一个j建立一个树状数组,每一次转移的时候求一个前缀和就行

dp值没更新就是0,更新了就更新

和之前做的一道线段树优化有点类似

其实就是看转移方程,涉及到求和,最值之类的

可以把状态作为下标,用数据结构维护dp值

#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;

typedef long long ll;
const int N = 1e5 + 10;
ll dp[N][15], f[15][N], a[N];
int n, k;

int lowbit(int x) { return x & (-x); }

void add(int id, int x, ll p)
{
	for(; x <= n; x += lowbit(x))	
		f[id][x] += p;
}

ll sum(int id, int x)
{
	ll res = 0;
	for(; x; x -= lowbit(x))
		res += f[id][x];
	return res;
}

int main()
{
	scanf("%d%d", &n, &k);
	_for(i, 1, n) scanf("%lld", &a[i]);
	
	ll ans = 0;
	_for(i, 1, n)
		_for(j, 1, k + 1)
		{
			if(j == 1) dp[i][j] = 1;
			else dp[i][j] = sum(j - 1, a[i] - 1);
			add(j, a[i], dp[i][j]);
			if(j == k + 1) ans += dp[i][j];
		}
	printf("%lld\n", ans);
	
	return 0;
}

poj 1269(求直线交点)

这题套模板就好了

get新知识点

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define REP(i, a, b) for(int i = (a); i < (b); i++) 
#define _for(i, a, b) for(int i = (a); i <= (b); i++) 
using namespace std;
 
const double eps = 1e-8;
 
struct point //点或向量 
{
	double x, y;
	point(double x = 0, double y = 0) : x(x), y(y) {}
};
 
point operator + (point a, point b) { return {a.x + b.x, a.y + b.y}; }
point operator - (point a, point b) { return {a.x - b.x, a.y - b.y}; }
point operator / (point a, double k) { return {a.x / k, a.y / k}; }
point operator * (point a, double k) { return {a.x * k, a.y * k}; }
double operator ^ (point a, point b) { return a.x * b.y - a.y * b.x; } 
double operator * (point a, point b) { return a.x * b.x + a.y * b.y; }
 
int dcmp(double x) 
{
	if(fabs(x) < eps) return 0;
	if(x > 0) return 1;
	return -1;	
} 
 
point make_point(point a, point b, point c, point d) //有交点的前提下求交点 
{
	double s1 = (a - c) ^ (d - c);
	double s2 = (b - c) ^ (d - c);
	return (b * s1 - a * s2) / (s1 - s2);
} 
 
int main()
{
	puts("INTERSECTING LINES OUTPUT");
	
	int n; scanf("%d", &n);
	_for(i, 1, n)
	{
		double x1, y1, x2, y2;
		double x3, y3, x4, y4;
		scanf("%lf%lf%lf%lf%lf%lf%lf%lf", &x1, &y1, &x2, &y2, &x3, &y3, &x4, &y4);
		
		point a(x1, y1), b(x2, y2);
		point c(x3, y3), d(x4, y4);
		point v1 = b - a, v2 = d - c;
		
		if(dcmp(v1 ^ v2) == 0)
		{
			if(dcmp((a - c) ^ v1) == 0) puts("LINE");
			else puts("NONE");
		}
		else
		{
			point t = make_point(a, b, c, d);
			printf("POINT %.2f %.2f\n", t.x, t.y);
		}
	}
	
	puts("END OF OUTPUT");
 
	return 0;
}

poj 1556 (直线方程 + dp)

这题关键在于我没想到要dp

大佬在旁边说了dp,我很快就想到答案了

求直线方程,求交点就是高中知识,手推一下就行了

端点作为状态的点,而直线看作转移状态

#include<cstdio>
#include<vector>
#include<cmath>
#include<cstring>
#define REP(i, a, b) for(int i = (a); i < (b); i++) 
#define _for(i, a, b) for(int i = (a); i <= (b); i++) 
using namespace std;

const int N = 1e3 + 10;
const double inf = 1e308;
double dp[N][10];
struct line { double x, y[5]; };
vector<line> ve;
int n;	

bool check(double x1, double y1, double x2, double y2)
{
	REP(i, 0, ve.size())
	{
		if(ve[i].x < min(x1, x2) || ve[i].x > max(x1, x2)) continue;
		double y = (y2 - y1) / (x2 - x1) * (ve[i].x - x1) + y1;
		if(y < ve[i].y[1] || ve[i].y[2] < y && y < ve[i].y[3] || y > ve[i].y[4]) return false;
	}
	return true;
}

double dis(double x1, double y1, double x2, double y2)
{
	return sqrt(pow(x1 - x2, 2) + pow(y1 - y2, 2));
}

int main()
{
	while(scanf("%d", &n) && n != -1)
	{
		memset(dp, 0, sizeof(dp));
		ve.clear();
		
		_for(i, 1, n)
		{
			line t;
			scanf("%lf", &t.x);
			_for(j, 1, 4) scanf("%lf", &t.y[j]);
			ve.push_back(t);
		}
		
		double ans = inf;
		REP(i, 0, ve.size())
			_for(j, 1, 4)
			{
				dp[i][j] = inf;
				if(check(0, 5, ve[i].x, ve[i].y[j]))
					dp[i][j] = min(dp[i][j], dis(0, 5, ve[i].x, ve[i].y[j]));
					
				REP(k, 0, i)
					_for(r, 1, 4)
						if(check(ve[k].x, ve[k].y[r], ve[i].x, ve[i].y[j]))
							dp[i][j] = min(dp[i][j], dp[k][r] + dis(ve[k].x, ve[k].y[r], ve[i].x, ve[i].y[j]));
				
				if(check(10, 5, ve[i].x, ve[i].y[j]))
					ans = min(ans, dp[i][j] + dis(10, 5, ve[i].x, ve[i].y[j]));
			}
			
		if(check(10, 5, 0, 5)) puts("10.00");
		else printf("%.2f\n", ans);
	}
	
	return 0;	
}

 

对于"吴恩达机器学习笔记2022"的问题,我没有在引用的内容中找到具体的相关信息。根据我所了解到的是,吴恩达是一位著名的机器学习专家,他在机器学习领域有很高的声誉,并且他的机器学习课程非常受欢迎。他的机器学习课程涵盖了从基础概念到实践应用的广泛内容,可以帮助学生建立起对机器学习的扎实理解并掌握相关的技能。如果您对具体的吴恩达的机器学习笔记2022有进一步的问题, 请提供更多的细节,我将尽力为您解答。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [【机器学习 吴恩达】2022课程笔记(持续更新)](https://blog.csdn.net/weixin_45837404/article/details/128709041)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [2022吴恩达机器学习课程学习笔记(第一课第一)](https://blog.csdn.net/Yang0114_/article/details/127400605)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值