Problem.A 花圃喷灌
本题灵感来源于2017年陕西省中考数学的几何压轴题,在算法题中来说这并不算是真正意义上的计算几何题,不过这题的数字被出题人弄得有点难算,但使用的几何定理都是初中数学的,比如垂径定理之类的,而最后的结果其实只需要一句输出,上过程(这题就不放代码了):
Problem.B 火车进出站
本题其实是数据结构的一个应用,就是求合法的出栈序列,当然也同时致敬一下铁路重庆站,字典序输出合法出栈序列,dfs没话说
vector<int> nums; //存储序列
stack<int> stk; //栈
int n, u, cnt;
void dfs()
{
if (nums.size() == n && cnt < 20)
{
for (auto it : nums)
cout << it << ' ';
cout << endl;
cnt++;
return;
}
if (stk.size())
{
nums.push_back(stk.top());
stk.pop();
dfs();
stk.push(nums.back());
nums.pop_back();
}
if (u <= n)
{
stk.push(u);
u++;
dfs();
stk.pop();
u--;
}
}
void solve()
{
cin >> n;
u = 1, cnt = 0;
dfs();
}
Problem.C 后宫风云
可以算是本场最难的题目,首先得根据一个题目背景跟图完全没关系的描述中想到二分图,染色法判断二分图是板子了,dfs和bfs都行,而这时又加了层二分,每个人看作一个点,每两个人的怨气值就是连接这两个点的无向边的权值,二分最大的怨气值,染色法判断能否形成二分图,最终找到能使二分图成立的最大怨气值,就是本题的思路,图论题的难点就是要能够发现这是考图论,其次就是建出图来,而图论题的大码量其实很多是板子
const int N = 20010, M = 200010;
int n, m;
int h[N], e[M], w[M], ne[M], idx;
int cor[N];
void add(int p, int x, int c)
{
e[idx] = x;
w[idx] = c;
ne[idx] = h[p];
h[p] = idx++;
}
//bfs判断二分图,也可用dfs,1,2分别代表两种颜色
bool bfs(int u, int mid)
{
queue<int> que;
cor[u] = 1;
que.push(u);
while (que.size())
{
int t = que.front();
que.pop();
for (int i = h[t];i != -1; i = ne[i])
{
int j = e[i];
if (w[i] > mid)
{
if (!cor[j])
{
cor[j] = 3 - cor[t];
que.push(j);
}
else if (cor[j] == cor[t]) return false;
}
}
}
return true;
}
//是否能够形成二分图
bool check(int mid)
{
memset(cor, 0, sizeof(cor));
for (int i = 1; i <= n; i++)
{
if (!cor[i] && !bfs(i, mid))
return false;
}
return true;
}
void solve()
{
memset(h, -1, sizeof(h));
cin >> n >> m;
int a, b, c;
int l = 0, r = 0;
while (m--)
{
cin >> a >> b >> c;
add(a, b, c);
add(b, a, c);
r = max(r, c);
}
while (l < r)
{
int mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
cout << l << endl;
}
Problem.E 骰子
判断是否同构,就根据两个骰子每个数字对面的数字是不是相同的来判定,由性质可以知道,三行数字中第一行第一个非0数字的对面一定是第三行第一个非0数字,第二行第一个非0数字一定和第二行第三的非0数字相对,而另外两个面就需要枚举一遍了,这题一定要理清思维,思路清晰了就问题不大了
const int N = 5;
int a[N][N], b[N][N];
int s1[10], s2[10];
void opp(int x[][5], int p[])
{
int temp = 0;
for (int j = 1; j <= 4; j++)
{
if (x[1][j]) temp = x[1][j];
}
for (int j = 1; j <= 4; j++)
{
if (x[3][j])
{
p[temp] = x[3][j];
p[x[3][j]] = temp;
break;
}
}
p[x[2][1]] = x[2][3];
p[x[2][3]] = x[2][1];
p[x[2][2]] = 7;
for (int i = 1; i <= 3; i++)
{
for (int j = 1; j <= 4; j++)
{
if (x[i][j] != 0 && p[x[i][j]] == 0)
{
p[x[i][j]] = x[2][2];
p[x[2][2]] = x[i][j];
break;
}
}
}
}
void solve()
{
for (int i = 1; i <= 3; i++)
for (int j = 1; j <= 4; j++)
cin >> a[i][j];
for (int i = 1; i <= 3; i++)
for (int j = 1; j <= 4; j++)
cin >> b[i][j];
opp(a, s1);
opp(b, s2);
for (int i = 1;i <= 6; i++)
{
if (s1[i] != s2[i])
{
cout << "NO" << endl;
return;
}
}
cout << "YES" << endl;
}
Problem.F Steve的打怪之路
这题也是比较难的,主要是题干信息比较多,读明白了就是一个二维费用的背包问题
令表示从前只怪物里选,使用的体力值不超过,损失的生命值不超过的最大打怪数量,打第只怪物需要消耗的体力为,需要损失的生命为,则可有以下划分
则状态转移方程为
这是01背包问题的二维费用问题,那么滚动数组还能够优化掉第一维,值得注意的是,这题中Steve的生命值不能到0,因此能够打败的最大怪物数量应该是,而要求此时损失的生命值们只需要第三维层层回退就能找到。
void solve()
{
cin >> s >> n >> m;
for (int i = 1; i <= s; i++)
{
cin >> w[i] >> v[i];
}
for (int i = 1; i <= s; i++)
for (int j = n; j >= w[i]; j--)
for (int k = m - 1; k >= v[i]; k--)
dp[j][k] = max(dp[j][k], dp[j - w[i]][k - v[i]] + 1);
cout << dp[n][m - 1] << ' ';
int k = m - 1;
while (k && dp[n][k - 1] == dp[n][m - 1]) k--;
cout << k << endl;
}