每日构造/DP(4.22)
E. Magic Stones
难点主要在想到用差分做。。
设 \(d\) 为 \(c\) 的差分数组,由 \(c′_i = c_{i+1} + c_{i−1} − c_i\) 移项可得: \(c′_i + c_{i−1} = c_{i+1} − c_i\)
因此对 \(c_i\) 进行一次操作相当于交换 \(d_i\) 与 \(d_{i+1}\) ,故将 \(c\) 与 \(t\) 的差分数组排序后判断是否相同即可。
#include <bits/stdc++.h>
#define IOS \
std::ios::sync_with_stdio(false); \
std::cin.tie(0); \
std::cout.tie(0);
#define PLL std::pair<ll, ll>
#define val first
#define id second
using ll = long long;
int main()
{
IOS;
int n;
std::cin >> n;
std::vector<ll> c(n + 1), t(n + 1), a(n + 1), b(n + 1);
for (int i = 1; i <= n; ++i)
std::cin >> c[i];
for (int i = 1; i <= n; ++i)
std::cin >> t[i];
for (int i = 1; i <= n; ++i)
a[i] = c[i] - c[i - 1], b[i] = t[i] - t[i - 1];
std::sort(a.begin() + 1, a.begin() + n + 1);
std::sort(b.begin() + 1, b.begin() + n + 1);
bool flag = true;
for (int i = 1; i <= n; ++i)
flag &= a[i] == b[i];
if (c[1] != t[1] || c[n] != t[n])
flag = false;
std::cout << (flag ? "Yes" : "No") << std::endl;
return 0;
}
P1941 飞扬的小鸟
设 \(f[i][j]\) 表示前 \(i\) 秒,高度为 \(j\) ,点击的最少次数,有转移方程:
\(f[i][j] = \underset {1 \leq k \leq \lfloor \frac{j}{x_i} \rfloor}{min}\{f[i - 1][j - k * x_i] + k,f[i - 1][j + y_i]\}\) ,复杂度为 \(O(n^3)\) ,考虑优化
不难发现上升与下降状态本质上就是完全背包 + 01背包,因此对于上升状态有新的转移方程:
\[f[i][j] = min
\begin{cases}
f[i - 1][j - x_i]& 在~i - 1~秒点击~1~次上升到~j\\
f[i][j - x_i]& 在~i - 1~秒点击~k~次上升到~j - x_i~,再点击~1~次到~j
\end{cases} + 1\]
除此之外细节也还蛮多的。。
#include <bits/stdc++.h>
#define IOS \
std::ios::sync_with_stdio(false); \
std::cin.tie(0); \
std::cout.tie(0);
const int inf = 0x3f3f3f3f;
int main()
{
IOS;
int n, m, k;
std::cin >> n >> m >> k;
std::vector<int> x(n + 1), y(n + 1), l(n + 1), h(n + 1), v(n + 1);
for (int i = 1; i <= n; ++i)
std::cin >> x[i] >> y[i];
for (int i = 1; i <= n; ++i)
l[i] = 1, h[i] = m;
for (int i = 1, p; i <= k; ++i)
{
std::cin >> p;
std::cin >> l[p] >> h[p];
++l[p], --h[p], v[p] = 1;
}
std::vector<std::vector<int>> f(n + 1, std::vector<int>(m + 1, inf));
for (int i = 0; i <= m; ++i)
f[0][i] = 0;
for (int i = 1; i <= n; ++i)
{
for (int j = 1; j <= m; ++j)
if (j > x[i])
f[i][j] = std::min(f[i][j], std::min(f[i - 1][j - x[i]], f[i][j - x[i]]) + 1);
for (int j = m - x[i]; j <= m; ++j) // 从 m - x[i] ~ m 开始点,最多到 m
f[i][m] = std::min(f[i][m], std::min(f[i - 1][j], f[i][j]) + 1);
for (int j = 1; j <= m; ++j)
if (j + y[i] <= m)
f[i][j] = std::min(f[i][j], f[i - 1][j + y[i]]);
for (int j = 1; j <= l[i] - 1; ++j)
f[i][j] = inf;
for (int j = h[i] + 1; j <= m; ++j)
f[i][j] = inf;
}
int min = inf;
for (int i = n, now = k; i; now -= v[i--])
{
for (int j = m; j; --j)
min = std::min(min, f[i][j]);
if (min < inf)
{
if (i == n)
{
std::cout << 1 << std::endl;
std::cout << min << std::endl;
return 0;
}
else
{
std::cout << 0 << std::endl;
std::cout << now << std::endl;
return 0;
}
}
}
std::cout << "0\n0";
return 0;
}