[紀中][總結]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;
}
相關文章
相關標籤/搜索