Aug.Day16 8.23总结

#A.填涂颜色

题目传送门

本题首先需要 推规律

nn \downarrow mm \rightarrow 2 3 4
3 k=3k=3 \rightarrow 66 k=24k=2 \rightarrow 4 k=24k=2\rightarrow4
4 k=38k=3 \rightarrow 8 k=39k=3 \rightarrow 9 k=516k=5 \rightarrow 16
5 k=510k=5 \rightarrow 10 k=412k=4 \rightarrow 12 k=39k=3 \rightarrow 9

最后终于发现规律:ans = min (n, k) * min (m, k)

Code:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e3 + 10;
int n, m, k, a[N][N];

void Fio (string iofile)
{
	string ifl = iofile + ".in", ofl = iofile + ".out";
	freopen (ifl.c_str (), "r", stdin);
	freopen (ofl.c_str (), "w", stdout);
}

signed main ()
{
	Fio ("color");
	cin >> n >> m >> k;
	cout << min (k, n) * min (k, m);
	return 0;
}

感受与学习:多推规律!!!(虽然但是这也需要那么一点点运气才能发现规律)。

错误原因:阿巴阿巴阿巴我没有那点运气(人话就是没发现规律)。


#B.密码

本题看似复杂,实际上 也不简单 非常简单。

只需要将一个转换数组 char cgcode[i][j] 求出来即可,所以我们需要发现规律。可以发现首先需要用双重循环 for (i, j : int ('A') ~ int ('Z'), int ('A') ~ int ('Z')) 枚举,发现当 i == 'A'cgcode[i][j] = j,而当 j == 'A' 时,cgcode[i][j] = i,接下来每一项 cgcode[i][j] 都是上一项加 11,所以可以写 cgcode[i][j - 1]cgcode[i - 1][j],都可以,不过需要判断一下当上一个为 'Z' 时,这一个应该为 'A'

接下来用 idx 表示密钥的下标,枚举密文的每一位,先考虑大小写的问题:由于 cgcode[i][j] 是全大写的,所以如果 code[i] 为小写字母,那么则用一个变量标记并转为大写,输出时再转为小写就行。

不过要注意,这里是将密文转为明文,所以输出时要用 for 循环在 cgcode[key[idx]] 中找到值为 code[i] 的第二维下标,下标即为明文(当然如果可以用公式也可以)。

Code:

#include <bits/stdc++.h>
using namespace std;
const int N = 5e2 + 10;
string key, code;
char cgcode[N][N];
int idx;

void Fio (string iofile)
{
	string ifl = iofile + ".in", ofl = iofile + ".out";
	freopen (ifl.c_str (), "r", stdin);
	freopen (ofl.c_str (), "w", stdout);
}

void InitCode ()
{
	int fra = int ('A'), toz = int ('Z');
	for (int i = fra; i <= toz; i ++)
	{
		for (int j = fra; j <= toz; j ++)
		{
			if (i == fra)
			{ cgcode[i][j] = j; continue; }
			if (j == fra)
			{ cgcode[i][j] = i; continue; }
			cgcode[i][j] = (cgcode[i - 1][j] + 1);
			if (cgcode[i - 1][j] == toz) cgcode[i][j] = fra;
		}
	}
}

int main ()
{
	Fio ("vigenere");
	cin >> key >> code;
	InitCode ();
	int fra = int ('A'), toz = int ('Z');
	for (int i = 0; i < code.size (); i ++)
	{
		bool issml = false;
		if (islower (code[i])) issml = true, code[i] = toupper (code[i]);
		if (islower (key[idx])) key[idx] = toupper (key[idx]);
		for (int j = fra; j <= toz; j ++)
			if (cgcode[key[idx]][j] == code[i])
			{
				cout << (issml ? char (j - 'A' + 'a') : char (j));
				break;
			}
		idx = (idx + 1) % key.size ();
	}
	return 0;
}

感受与学习:在写一道题写完后,首先检查正确性,其次检查语法错误,最后找一些奇奇怪怪的错误,比如在 Windows 环境下可以运行的代码到 Linux 环境下不能运行的错误,由于 CSP 评测时使用 NOI Linux 环境编译,所以 这点非常重要!!!

错误原因:qwq 本来用来初始化 cgcode 的函数类型想写 void 写成了 char,虽然 Dev-C++ 上能运行但是 Linux 环境不能通过...


#C.海港

本题真的非常非常简单而煎蛋。( 所以就不讲了 )。

首先本题需要看懂题面,看懂了就很好做了。这是一道模版滑动窗口(呃由于太过简单所以没觉得是滑动窗口),作者用了仨 STL 写,个人认为就是一道 小W的病毒歼灭战++,思路都有些十分相似。

首先需要一个 unordered_map 记录每种国籍的人有多少人和一个变量 cnt 记录有多少种不同的国籍,再用一个双端队列 + vector 记录每艘船的信息,队列中的每一个元素就是一艘船,vector 中第 00 位是这艘船进港的时间,1v11 \sim |v|-1v|v| 表示 vector 的大小)表示每个乘客的国籍,每新进一艘船就将其和队首元素作比较,如果大于等于 8640086400 则遍历队首的 vector,将每位乘客的国籍编号在 unordered_map 中减 11,如果发现有一次减完了,则 cnt --

接下来将当前船只入队并遍历当前船只的所有乘客 v[i],将每个乘客在 unordered_map 中的变量加 11,如果发现某个国籍的人在加 11 之前是 00,那么说明有新国籍的人进来,cnt ++

最后每次输出 cnt 即可。

Code:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 10;
unordered_map <int, int> sta;
deque <vector<int> > ct;
int n, cnt;

void Fio (string iofile)
{
	string ifl = iofile + ".in", ofl = iofile + ".out";
	freopen (ifl.c_str (), "r", stdin);
	freopen (ofl.c_str (), "w", stdout);
}

signed main ()
{
	Fio ("port");
	cin >> n;
	for (int i = 1; i <= n; i ++)
	{
		int t, k; cin >> t >> k;
		ct.push_back ({t});
		for (int j = 1; j <= k; j ++)
		{
			int x; cin >> x;
			if (! sta[x]) cnt ++;
			sta[x] ++;
			ct.back ().push_back (x);
		}
		if (ct.size () > 1)
			while (ct.size () > 1 && ct.back ()[0] - ct.front ()[0] >= 86400)
			{
				for (int j = 1; j < ct.front ().size (); j ++)
				{
					sta[ct.front ()[j]] --;
					if (! sta[ct.front ()[j]]) cnt --;
				}
				ct.pop_front ();
			}
		cout << cnt << '\n';
	}
	return 0;
}

感受与学习:要学会灵活运用 STL 中的数据结构,明白每种数据结构的用处。

错误原因:无。


#D.流星雨

题目传送门

本题就是一道广搜模版题(虽然但是我又双叒叕没做出来)。

首先用一个 g[][] 数组记录流星落下的时间,没有则赋值为 0x3f3f3f3f,不过记住要更新它和它周围的 44 个格子并考虑边界问题。

接下来套一个广搜模版,只不过需要改一点条件:

  • 结束的条件需要改变,只要当前走到的 g[x][y]0x3f3f3f3f 说明走到了安全的地方,直接 return
  • 广搜往四个方向搜时,continue 的条件要变,首先要判断边界,这里有一个细节,nxny 的边界为 03000 \sim 300,因为输入的 x, y 的最大值为 300300。其次遍历当前的步数是否小于当前位置流星砸下来的时间(因为步数就是用时),如果都满足,就新增当前节点。
  • 最后特判一下,如果在 00 时刻点 (0,0)(0,0) 就有陨石砸下来那么直接输出 -1(作者也不太确定有没有用)。

Code:

#include <bits/stdc++.h>
#define x first
#define y second
using namespace std;
int dx[] = {-1, 0, 1, 0};
int dy[] = {0, 1, 0, -1};
int n, mnu = 2e9;
bool vis[310][310];
int g[310][310];

struct meteoJ
{
	int x, y;
	int step;
};

queue <meteoJ> mor;

void Fio (string iofile)
{
	string ifl = iofile + ".in", ofl = iofile + ".out";
	freopen (ifl.c_str (), "r", stdin);
	freopen (ofl.c_str (), "w", stdout);
}

void BFSmt ()
{
	mor.push ({0, 0, 0});
	while (mor.size ())
	{
		auto now = mor.front (); mor.pop ();
		if (g[now.x][now.y] == 0x3f3f3f3f)
		{
			mnu = now.step;
			return;
		}
		for (int i = 0; i < 4; i ++)
		{
			int nx = now.x + dx[i];
			int ny = now.y + dy[i];
			int ns = now.step + 1;
			if (nx < 0 || nx > 301 || ny < 0 || ny > 301) continue;
			if (ns >= g[nx][ny]) continue;
			if (vis[nx][ny]) continue;
			vis[nx][ny] = true;
			mor.push ({nx, ny, ns});
		}
	}
    mnu = -1;
}

int main ()
{
	Fio ("meteor");
	cin >> n;
	memset (g, 0x3f, sizeof (g));
	for (int i = 1; i <= n; i ++)
	{
		int x, y, t; cin >> x >> y >> t;
		g[x][y] = min (t, g[x][y]);
		if (x + 1 < 301) g[x + 1][y] = min (t, g[x + 1][y]);
		if (y + 1 < 301) g[x][y + 1] = min (t, g[x][y + 1]);
		if (x - 1 > -1) g[x - 1][y] = min (t, g[x - 1][y]);
		if (y - 1 > -1) g[x][y - 1] = min (t, g[x][y - 1]);
	}
	BFSmt ();
    if (g[0][0] == 0) { cout << -1; return 0; }
	cout << (mnu == 0x3f3f3f3f ? -1 : mnu);
	return 0;
}

感受与学习:qwq 以后遇到这种又放在靠后又看起来复杂、涉及很难的算法的题应该仔细阅读,模版题的变形不一定就很难。

错误原因:qwq 有两点:

  1. 阿巴阿巴因为就像上面说的原因,觉得又放在靠后又看起来复杂、涉及很难的算法(自己绝对做不出来的那种)。
  2. 没时间力...