2021.9.26 模拟赛解题报告

T1 珠江夜游

题解: 二分最终到达的时间,然后判断该时间是否合法,开一个数组记录每个点距离目标点的位置,第一点因为没有阻挡,先算出来,然后考虑后边的船。

对于在后边的船要考虑前边的船是否挡着它,有两种情况:

  1. 前边的船比当前船慢,那么最终距离就是前边船的最终距离加上前边船的船长。
  2. 前边的船比当前船快,在前边的船虽然比它快,但是前边的船有可能挡着后边的船使它到某一位置之后不能向前移动,所以要和前边船的最终距离 加 船长 与 当前船能到达的最终距离取 max。
/*
Date:
Source:
knowledge:
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#define orz cout << "AK IOI" << "
"
#define Min(x, y) ((x) < (y) ? (x) : (y))
#define Max(x, y) ((x) > (y) ? (x) : (y))
#define int long long 

using namespace std;
const int maxn = 1e5 + 10;
const double eps = 1e-4;

inline int read()
{
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
inline void print(int X)
{
	if(X < 0) X = ~(X - 1), putchar('-');
	if(X > 9) print(X / 10);
	putchar(X % 10 ^ '0');
}
int T, n;
double l[maxn], x[maxn], v[maxn], a[maxn];
bool check(double mid)
{
	for(int i = 1; i <= n; i++) a[i] = x[i];
	a[n] = a[n] - mid * v[n];
	for(int i = n - 1; i >= 1; i--)
	{
		double tim = mid * v[i];
		a[i] = max(a[i] - tim, a[i + 1] + l[i + 1]);//最终能到达的里目标的位置 
		if(a[i] > 1e-3) return 0;
	}
	return 1;
}
signed main()
{
	//freopen("cruise.in","r",stdin);
    //freopen("cruise.out","w",stdout);
	T = read();
	while(T--)
	{
		n = read(); n++;
		for(int i = 1; i <= n; i++) scanf("%lf", &l[i]);
		for(int i = 1; i <= n; i++) scanf("%lf", &x[i]);
		for(int i = 1; i <= n; i++) scanf("%lf", &v[i]);
		double l = 0, r = 1000000000;
	 	while(r - l > eps) 
	 	{
	 		double mid = (l + r) / 2.0;
	 		if(check(mid)) r = mid;
	 		else l = mid;
		}
		printf("%.3lf
", r);
	}
	//fclose(stdin);
	//fclose(stdout);
	return 0;
}
/*
2
1
2 2 
7 1 
2 1 
2
1 2 2 
10 7 1 
6 2 1

*/

T2 旅行计划

简化题意:

对于一个图, 问最少几笔可以将所有的边经过一次,且每条边仅经过一次。

题解:

每个连通块是独立的,对于每一个连通块,如果奇度数点的个数为 (k)​​,那么至少需要 $ max(frac k2, 1)$​​ 条路径。因为是在无向图中,将两个度数为奇数的点连在一起,就可以消去两个奇数点。

所以要将一个无向图变的有欧拉回路,就要加 (frac k2)​ 条边。

我们只要将一个连通块变成欧拉图,搜一遍得到路径,再将我们人为添加的边删掉,剩下的就是这个连通块的"笔画"了。

为什么是欧拉回路而不是欧拉通路呢?

其实结果是一样的只是算法不同。

于为什么这里加 k/2 条边后扫出的结果删掉多加的边就是答案,并且这个答案是 $ max(frac k2, 1)$呢?

这是因为一个图中,我们从奇数点开始走,停止的也一定是在奇数点,那么再一遍遍不重复地从图中走出一条条类似前面地路径(奇数点开始,奇数点结束)然后我们将这些路径地终点连接下一条路径的起点(多加的边),这样又因为每一条路径的起止点都为奇数点,一条边删去两个奇数点。所以最终把他们连成欧拉回路所需的边的数量就是 (frac k2)

/*
Date:
Source:
konwledge:
*/
#include <iostream>
#include <cstdio>
#include <stack>
#include <cstring>
#include <vector>
#define orz cout << "AK IOI" << "
"
#define int long long

using namespace std;
const int maxn = 5e5 + 10;
const int inf = 0x3f3f3f3f;

inline int read()
{
	int f = 0, x = 0;
	char ch = getchar();
	while(!isdigit(ch)) f |= (ch == '-'), ch = getchar();
	while(isdigit(ch)) x = x * 10 + (ch ^ 48), ch = getchar();
	return f ? -x : x;
}
inline void print(int X)
{
	if(X < 0) X = ~(X - 1), putchar('-');
	if(X > 9) print(X / 10);
	putchar(X % 10 ^ '0');
}
int n, m, du[maxn], vis[maxn], ans1;
struct node{
	int u, v, w, nxt, vis;
}e[maxn << 1];
int js, head[maxn];
void add(int u, int v, int w)
{
	e[++js] = (node){u, v, w, head[u], 0};
	head[u] = js;
}
void init()
{
	js = 0, ans1 = 0;
	for(int i = 1; i <= n; i++) head[i] = vis[i] = du[i] = 0;
	for(int i = 1; i <= js; i++) e[i].vis = 0;
	/*memset(head, 0, sizeof head);
	memset(vis, 0, sizeof vis);
	memset(du, 0, sizeof du);*/
}
vector <int> b[maxn];
void dfs(int u)
{
	vis[u] = 1;
	for(int i = head[u]; i; i = e[i].nxt)
	{
		int v = e[i].v;
		if(e[i].vis) continue;
		e[i].vis = 1;
		if(i % 2 != 0) e[i + 1].vis = 1; 
		else e[i - 1].vis = 1;//对于双向边的处理
		dfs(v);
		if(e[i].w != inf) b[ans1].push_back(e[i].w);
		else ans1++;
	}
}
signed main()
{
	//freopen("travelling1.in","r",stdin); 
	//freopen("travelling.out","w",stdout);
	int T = read();
	while(T--)
	{
		init();
		n = read(), m = read();
		for(int i = 1; i <= m; i++)
		{
			int u = read(), v = read();
			add(u, v, i), add(v, u, -i); 
			du[u]++, du[v]++;
		}
		int now = 0;
		for(int i = 1; i <= n; i++)
		{
			if(du[i] % 2 != 0)
			{
				if(now) add(now, i, inf), add(i, now, inf), now = 0;
				else now = i;
			}
		}
		for(int i = 1; i <= n; i++)
			if(!vis[i] && du[i] % 2 != 0) ans1++, dfs(i), ans1--;
		for(int i = 1; i <= n; i++)
			if(!vis[i] && du[i]) ans1++, dfs(i);
		printf("%lld
", ans1 - 1);
		for(int i = 1; i <= ans1; i++)
		{
			printf("%lld ", b[i].size());
			for(int j = 0; j < b[i].size(); j++) printf("%lld ", -b[i][j]);
			puts("");
			b[i].clear();
		}
	}
	//fclose(stdin);
	//fclose(stdout);
	return 0;
}
/*
1
3 3
1 2
1 3
2 3
*/

T3 基站建设

(f[i][j][k][l])​ 来表示当前矩阵左上角为(i)​,(j)​, 右下角为(k)(l) 的矩阵的最优解.
可以先从最小的一行来看,我们不会分着去取,以为那样的贡献会非常大,然后矩阵上也一样.
我们一定是取一个矩阵,然后不断地加一行或者一列,然后我们枚举四边加一行或一列需要的
贡献,然后取一个最小值,然后加上去。

然后题解中讲了一个曼哈顿距离转切比雪夫距离。

设点 (P_1(x_1, y_2)) , (P_2(x_2, y_2)), 令 (p_1' (x_1 + y_1, x_1 - y_1)), (P_2' (x_2 + y_2, x_2 - y_2))​。

借助恒等式 $max(|a|, |b|) = ( |a + b|, |a – b| ) / 2 $​

可以推出

(P_1’)​​ 与 $ P_2’$​​间的切比雪夫距离

$= max(|(x_1 – x_2) + (y_1–y_2)|, |(x_1–x_2) – (y_1–y_2)| ) = |x_1 – x_2| + |y_1 – y_2| $​

= (P_1)​与 (P_2)​间的曼哈顿距离

/*
Date:
Source:
knowledge:
*/
#include <iostream>
#include <cstdio>
#define orz cout << "AK IOI" << "
"
#define Min(x, y) ((x) < (y) ? (x) : (y))
#define Max(x, y) ((x) > (y) ? (x) : (y))

using namespace std;

inline int read()
{
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
inline void print(int X)
{
	if(X < 0) X = ~(X - 1), putchar('-');
	if(X > 9) print(X / 10);
	putchar(X % 10 ^ '0');
}
int f[10][10][10][10];
char s[10][10];
int calc(int x1, int x2, int y1, int y2, int xx1, int xx2, int yy1, int yy2) 
{
	int mnx = 1e9, mxx = 0, mny = 1e9, mxy = 0;
	for(int i = xx1; i <= xx2; ++i)
		for(int j = yy1; j <= yy2; ++j)
			if(s[i][j] == '#') 
			{
				mnx = min(mnx, i);
				mxx = max(mxx, i);
				mny = min(mny, j);
				mxy = max(mxy, j);
			}
	if(mxx == 0) return 0;
	int ans = 0;
	for(int i = x1; i <= x2; ++i)
		for(int j = y1; j <= y2; ++j)
			if(s[i][j] == '#') ans += max(abs(i - mnx), max(abs(i - mxx), max(abs(j - mny), abs(j-mxy))));
	return ans;
}

int dfs(int x1, int y1, int x2, int y2) 
{
	if(x1 > x2 || y1 > y2) return 0;
	if(f[x1][y1][x2][y2] != -1) return f[x1][y1][x2][y2];
	int ret = dfs(x1 + 1,y1,x2,y2) + calc(x1,x1,y1,y2,x1,x2,y1,y2);
	ret = min(ret,dfs(x1,y1 + 1,x2,y2) + calc(x1,x2,y1,y1,x1,x2,y1,y2));
	ret = min(ret,dfs(x1,y1,x2 - 1,y2) + calc(x2,x2,y1,y2,x1,x2,y1,y2));
	ret = min(ret,dfs(x1,y1,x2,y2 - 1) + calc(x1,x2,y2,y2,x1,x2,y1,y2));
	return f[x1][y1][x2][y2] = ret;
}

int main() 
{
//	freopen("station.in","r",stdin);
//	freopen("station.out","w",stdout);
	for(int i = 1; i <= 8; ++i) scanf("%s", s[i] + 1);
	memset(f, -1, sizeof(f));
	printf("%d", dfs(1, 1, 8, 8));
	return 0;
}

其实代码不是我的,是zsf学长的。

原文地址:https://www.cnblogs.com/yangchengcheng/p/15339493.html