[纪中][总结]2021.07.12【NOIP提升B组】模拟

2021.07.12【NOIP提升B组】模拟




  1. T1题面传送门
  2. T2题面传送门
  3. T3题面传送门
  4. T4题面传送门

因为不够方便, 因此将 \(T1\)\(T4\) 的题面放到其余里面了, 请谅解!
还有模拟赛的连接 传送门html


题目解析

T1

咱们能够这么想, 既然要求 \(A_m + A_n + A_p = A_i\), 那么根据 \(n^3\) 的算法来算 \(5000\)\(n\) 是绝对会超时的, 因此咱们考虑用小学的知识 : 移项
可得 \(A_m + A_n = A_i - A_p\)
这样就能够二维处理 \(A_m + A_n\) 再存进 \(hash\) 表里面, 再用 \(A_i - A_p\) 去匹配。 若是匹配成功了, 那么就存在 \(A_m + A_n + A_p = A_i\)\(ans\)\(++\)c++

Code (T1)

#include <bits/stdc++.h>
#define N 5005 
#define M 25000004
#define hu 689207157
#pragma GCC optimize(2)
using namespace std;

int n, ans, flag;
int a[N], hash[M];

int hhd (int x)
{
	int te = (x % M + M) % M;
	int i = 0;
	while (i < M && hash[(te + i) % M] != x && hash[(te + i) % M] != hu) i ++;
	return (te + i) % M;
}

void insert_hhd (int x)
{
	int dw = hhd (x);
	hash[dw] = x;
}

int main ()
{
	freopen ("good.in", "r", stdin);
	freopen ("good.out", "w", stdout);
	scanf ("%d", &n);
	for (int i = 0; i < M; ++ i) hash[i] = hu;
	for (int i = 1; i <= n; ++ i)
	{
		flag = 0;
		scanf ("%d", &a[i]);
		for (int j = 1; j < i; ++ j)
		{
			if (hash[hhd(a[i] - a[j])] == a[i] - a[j] && !flag)
			{
				ans ++;
				flag = 1;
				break;
			}
		}
		for (int j = 1; j <= i; ++ j) insert_hhd (a[i] + a[j]);
	}
	printf ("%d", ans);
	return 0;
}

T2

能够理解为一排排好序的数组, 每一个点都有x, y坐标。
因此咱们能够用经典的欧氏距离来求出两个点之间的距离。算法

而后注意题目要求走彻底部点数组

每一个点通过一次闭包

而后能够把来去理解为从 \(A\) 点出发, 走两个不相交的路径到达点 \(B\), 因而这道题又能够转化为
两条不相交路径最短问题
而后就是 \(DP\), 设状态 \(f[i][j]\) 为从点 \(A\) (编号\(1\)) 的两条路径分别走到 编号为 \(i\) 和 编号为 \(j\) 的两个点, 再注意判断一些特殊的点便可。
注意, 要杜绝 \(i == j\) 的状况 (当 \(i == j == n\)的状况除外, 由于这样是走到终点了)。
还有要继续往前就用一个 \(Max(i, j) + 1\) 便可, 即当前所走的最大编号的点再往前走一步spa

Code (T2)

#include <bits/stdc++.h>
#define db double 
#define N 1005 
using namespace std;

int n, b1, b2;
db hhd[N][3], f[N][N];

db dis (int hu, int he) // 传统的欧氏距离
{
	db x = (db)(hhd[hu][1] - hhd[he][1]) * (hhd[hu][1] - hhd[he][1]);
	db y = (db)(hhd[hu][2] - hhd[he][2]) * (hhd[hu][2] - hhd[he][2]);
	return (db)sqrt (x + y);
} 

void run ()
{
	int i, j;
	for (int i = 1; i <= n; ++ i)
	 for (int j = 1; j <= n; ++ j)
	  f[i][j] = 123456789;
	f[1][1] = 0;
	for (int i = 1; i <= n; ++ i)
	 for (int j = 1; j <= n; ++ j)
	 {
	 	if (i != j || i == 1)
	 	{
	 	 	int k = max (i, j) + 1;
			if (k == n + 1)
			{
				if (j == n) f[n][n] = min (f[n][n], f[i][n] + dis (i, n));
				if (i == n) f[n][n] = min (f[n][n], f[n][j] + dis (j, n));
			}	
			else
			{
				if (k != b1) f[i][k] = min (f[i][k], f[i][j] + dis(j, k));  //回的时候不走b1 
                if (k != b2) f[k][j] = min (f[k][j], f[i][j] + dis(i, k));
			}
		}
	 }
	 printf ("%.2lf", f[n][n]);
}

int main ()
{
	freopen ("path.in", "r", stdin);
	freopen ("path.out", "w", stdout);
	scanf ("%d%d%d", &n, &b1, &b2);
	b1 += 1, b2 += 1; //  编号是从0开始的
	for (int i = 1; i <= n; ++ i) scanf ("%lf%lf", &hhd[i][1], &hhd[i][2]); 
	run ();
	return 0;
}

T3

虽然没有调出来, 可是思路我仍是很清晰的~.net

有一个前提的基础, 以下图
前提的基础
而后就不会了
看看机房dalao的博客传送门code

T4

第一个思路, 暴力;
能够跑 \(Dijstkra\) 来肯定哪些点是否相通, 而后再暴力枚举, 判断最多能有多少个不相通且两两间不相邻的点。htm

正解 : 传递闭包。
其实就是一个二分图的模板加上 \(floyd\) 就能过了(数据水?)blog

Code (T4)

#include <bits/stdc++.h>
#define N 205 
using namespace std;

int n, m, ans;
int G[N][N], vis[N], link[N];

int found (int x)
{
	for (int i = 1; i <= n; ++ i)
		if (!vis[i] && G[x][i])
		{
			vis[i] = 1;
			if (!link[i] || found (link[i]))
			{
				link[i] = x;
				return 1;
			}
		}
	return 0;
}
int main ()
{
	scanf ("%d%d", &n, &m);
	for (int i  =1; i <= m; ++ i)
	{
		int x, y;
		scanf ("%d%d", &x, &y);
		G[x][y] = 1;
	}
	for (int k = 1;k <= n; ++ k)
		for (int i = 1; i <= n; ++ i)
			for (int j = 1; j <= n; ++ j)
				if (G[i][k] && G[k][j])
					G[i][j] = 1;
	for (int i = 1; i <= n; ++ i)
	{
		memset (vis, 0, sizeof (vis));
		ans += found (i);
	}
	printf ("%d", n - ans);
	return 0;
}
相关文章
相关标签/搜索