AtCoder Beginner Contest 283
《E - Don't Isolate Elements》
dp
刚开始拿到这道题时,我总是在想:第一行翻不翻转对下面情况的影响,在什么情况下要反转,等一系列情况
最后我发现:这些情况不如我可以利用状态转移来实现,于是我朝着dp方向想。
一开始我设置的dp是dp[i][j]:在第i-1行即以上行都合法,而且使第i行也合法同时第i行翻不翻转的状态为j的最小操作次数
但是这样设置我下面就面临了问题:
1.第i行的合法性与第i-1行和第i+1行是相关的,
2.既然第i行的合法性与第i+1行是相关的,我又如何能够保证在第i+1还没被算出来之前,第i行是能够提前被算出来的呢?
即我状态转移不知道写。
3.我如何确定合法性?
解决:
dp[i]...:首先这里我便要重新定义:既然要知道第i+1行的状态才能知道第i行合法性,那便i表示保证第i-1以及以上行的合法性
,但是不保证第i行的合法性。
在状态转移的过程中,比如从第i-1行转移到第i行该如何转移?
因为dp[i-1]...不保证第i-1行合法性,我必须枚举第i行的状态和第i-1行的状态,以及要知道第i-2行的状态才能知道,第i-1行到底合不合法
于是:
dp[i][j][k]:表示保证第i-1以及以上行的合法性,并且第i-1行的状态为j,第i行的状态为k的最小操作次数
如果第i-1行在第i-2行状态为u,第i-1行状态为j,第i行状态为k的情况下,是合法的,那么进行状态转移:
k只有0,1两个状态
dp[i][j][k]=min(dp[i][j][k],dp[i-1][u][j]+k)
如何判断是否合法看check()函数
1 #include <iostream>
2 #include <algorithm>
3 #include <cstring>
4 using namespace std;
5 const int INF = 0x3f3f3f3f;
6 const int N = 1005;
7 // dp[i][j][k]:表示在第i行,确保第i-1行即以上的行都是合法的情况下
8 // 到达第i行状态是k,第i-1行状态是j的情况的最小操作数;
9 int dp[N][2][2], n, m;
10 int a[N][N];
11 // 在第i-2行状态为u,第i-1行状态为j,第i行状态为k的情况下,第i-1行是否合法
12 bool check(int u, int j, int k, int h)
13 {
14 for (int i = 1; i <= m; i++)
15 {
16 int ns = j ? !a[h][i] : a[h][i];
17 int os;
18 if (h - 1 >= 1)
19 {
20 os = u ? !a[h - 1][i] : a[h - 1][i];
21 if (ns == os)
22 continue;
23 }
24 if (i + 1 <= m)
25 {
26 os = j ? !a[h][i + 1] : a[h][i + 1];
27 if (ns == os)
28 continue;
29 }
30 if (h + 1 <= n)
31 {
32 os = k ? !a[h + 1][i] : a[h + 1][i];
33 if (ns == os)
34 continue;
35 }
36 if (i - 1 >= 1)
37 {
38 os = j ? !a[h][i - 1] : a[h][i - 1];
39 if (ns == os)
40 continue;
41 }
42 return false;
43 }
44 return true;
45 }
46 int main()
47 {
48 cin >> n >> m;
49 for (int i = 1; i <= n; i++)
50 for (int j = 1; j <= m; j++)
51 scanf("%d", &a[i][j]);
52 memset(dp, 0x3f, sizeof(dp));
53 dp[1][0][0] = 0;
54 dp[1][1][0] = 0;
55 dp[1][0][1] = 1;
56 dp[1][1][1] = 1;
57 // 0代表不反转,1代表反转
58 for (int i = 2; i <= n; i++)
59 // 第i-1行状态:
60 for (int j = 0; j <= 1; j++)
61 // 第i-2行状态:
62 for (int u = 0; u <= 1; u++)
63 // 第i行状态:
64 for (int k = 0; k <= 1; k++)
65 {
66 if (check(u, j, k, i - 1))
67 dp[i][j][k] = min(dp[i][j][k], dp[i - 1][u][j] + k);
68 }
69 int ans = INF;
70 for (int i = 0; i <= 1; i++)
71 for (int j = 0; j <= 1; j++)
72 for (int k = 0; k <= 1; k++)
73 {
74 if (check(i, j, k, n))
75 ans = min(ans, dp[n][i][j]);
76 }
77 if (ans >= INF)
78 cout << -1;
79 else
80 cout << ans;
81 return 0;
82 }
《F - Permutation Distance 》
偏序问题
来自大佬博客:https://www.cnblogs.com/Lanly/p/17003594.html
可以看出其是与逆序对这样的二维偏序问题很像
https://www.cnblogs.com/cilinmengye/p/17008988.html
只是逆序对,用持久化数据结构维护的是区间内数的个数
这里我们要维护的是区间数的最小值,用线段树维护:
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 2 * 1e5 + 5, INF = 0x3f3f3f3f;
struct tree
{
int k1, k2;
int l, r;
} tr1[N * 4], tr2[N * 4];
void pushup1(int u)
{
tr1[u].k1 = min(tr1[u << 1].k1, tr1[u << 1 | 1].k1);
tr1[u].k2 = min(tr1[u << 1].k2, tr1[u << 1 | 1].k2);
}
void pushup2(int u)
{
tr2[u].k1 = min(tr2[u << 1].k1, tr2[u << 1 | 1].k1);
tr2[u].k2 = min(tr2[u << 1].k2, tr2[u << 1 | 1].k2);
}
void build(struct tree tr[], int x, int l, int r)
{
tr[x].k1 = tr[x].k2 = INF;
if (l == r)
{
tr[x].l = tr[x].r = l;
return;
}
tr[x].l = l, tr[x].r = r;
int mid = (l + r) >> 1;
build(tr, x << 1, l, mid);
build(tr, x << 1 | 1, mid + 1, r);
}
void modify1(int u, int x, int p, int i)
{
if (tr1[u].l == x && tr1[u].r == x)
{
tr1[u].k1 = -(p + i);
tr1[u].k2 = -(-p + i);
return;
}
int mid = (tr1[u].l + tr1[u].r) >> 1;
if (x <= mid)
modify1(u << 1, x, p, i);
else
modify1(u << 1 | 1, x, p, i);
pushup1(u);
}
void modify2(int u, int x, int p, int i)
{
if (tr2[u].l == x && tr2[u].r == x)
{
tr2[u].k1 = -(p - i);
tr2[u].k2 = -(-p - i);
return;
}
int mid = (tr2[u].l + tr2[u].r) >> 1;
if (x <= mid)
modify2(u << 1, x, p, i);
else
modify2(u << 1 | 1, x, p, i);
pushup2(u);
}
int query1(struct tree tr[], int u, int l, int r)
{
if (tr[u].l >= l && tr[u].r <= r)
return tr[u].k1;
int ans = INF;
int mid = (tr[u].l + tr[u].r) >> 1;
if (l <= mid)
ans = min(ans, query1(tr, u << 1, l, r));
if (r > mid)
ans = min(ans, query1(tr, u << 1 | 1, l, r));
return ans;
}
int query2(struct tree tr[], int u, int l, int r)
{
if (tr[u].l >= l && tr[u].r <= r)
return tr[u].k2;
int ans = INF;
int mid = (tr[u].l + tr[u].r) >> 1;
if (l <= mid)
ans = min(ans, query2(tr, u << 1, l, r));
if (r > mid)
ans = min(ans, query2(tr, u << 1 | 1, l, r));
return ans;
}
int ps[N], n;
int ans[N];
int main()
{
cin >> n;
build(tr1, 1, 1, n);
build(tr2, 1, 1, n);
for (int i = 1; i <= n; i++)
scanf("%d", &ps[i]);
for (int i = 1; i <= n; i++)
{
int p = ps[i];
ans[i] = min(p + i + query1(tr1, 1, 1, p - 1), -p + i + query2(tr1, 1, p + 1, n));
modify1(1, p, p, i);
}
for (int i = n; i >= 1; i--)
{
int p = ps[i];
ans[i] = min(min(ans[i], p - i + query1(tr2, 1, 1, p - 1)), -p - i + query2(tr2, 1, p + 1, n));
modify2(1, p, p, i);
}
for (int i = 1; i <= n; i++)
cout << ans[i] << " ";
cout << endl;
return 0;
}
本文作者:次林梦叶
本文链接:https://www.cnblogs.com/cilinmengye/p/17008799.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步