Educational Codeforces Round 20 E. Roma and Poker
差分约束
我们记W
表示\(1\),L
表示\(-1\),D
表示\(0\),然后记前\(i\)位的前缀和是\(dis[i]\)。则我们可以根据题面得到如下约束。
当前位是W
,则有
\[\left\{\begin{matrix}
dis[i] - dis[i-1] \le 1 \\
dis[i-1] - dis[i] \le -1
\end{matrix}\right.
\]
当前位是L
,则有
\[\left\{\begin{matrix}
dis[i] - dis[i-1] \le -1 \\
dis[i-1] - dis[i] \le 1
\end{matrix}\right.
\]
当前位是D
,则有
\[\left\{\begin{matrix}
dis[i] - dis[i-1] \le 0 \\
dis[i-1] - dis[i] \le 0
\end{matrix}\right.
\]
前缀和的绝对值小于\(k\),则有
\[\left\{\begin{matrix}
dis[i] - dis[0] \le k-1 \\
dis[i] - dis[i] \le k-1
\end{matrix}\right.
\]
所有位置的和的绝对值等于\(k\),则有
\[\left\{\begin{matrix}
dis[0] - dis[n] \le -k \\
dis[n] - dis[0] \le k
\end{matrix}\right.
\or\left\{\begin{matrix}
dis[0] - dis[n] \le k \\
dis[n] - dis[0] \le -k
\end{matrix}\right.
\]
考虑除了绝对值等于\(k\)外的约束都是唯一的,因此我们可以在最后先加入绝对值等于\(k\)的其中一种约束,跑一遍差分约束后,删边,再加入另一种约束,再跑一遍差分约束。如果都没有解,就是无解。
#include <bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
#define int i64
using vi = vector<int>;
using pii = pair<int,int>;
const i64 inf = LLONG_MAX / 2;
i32 main(){
ios::sync_with_stdio(false), cin.tie(nullptr);
int n, k;
cin >> n >> k;
string s;
cin >> s;
s = " " + s;
vector<vector<pii>> e(n + 1);
for(int i = 1; i <= n; i ++) {
if(s[i] == 'W') {
e[i].emplace_back(i - 1, -1);
e[i - 1].emplace_back(i , 1);
} else if(s[i] == 'L') {
e[i].emplace_back(i - 1, 1);
e[i - 1].emplace_back(i, -1);
} else if(s[i] == 'D') {
e[i].emplace_back(i - 1, 0);
e[i - 1].emplace_back(i, 0);
} else {
e[i].emplace_back(i - 1, 1);
e[i - 1].emplace_back(i, 1);
}
}
for(int i = 1; i < n; i ++) {
e[i].emplace_back(0, k - 1);
e[0].emplace_back(i, k - 1);
}
vi dis;
auto bellman_ford = [n, &e, &dis]() -> bool {
vi vis(n + 1, 0), tot(n + 1, 0);
dis = vi(n + 1, inf);
dis[0] = 0, vis[0] = 1;
queue<int> q;
q.push(0);
while(not q.empty()) {
int x = q.front();
q.pop();
vis[x] = 0;
for(auto [y, w] : e[x]) {
if(dis[y] <= dis[x] + w) continue;
dis[y] = dis[x] + w;
if(vis[y]) continue;
if(++ tot[y] == (n + 1)) return false;
vis[y] = 1;
q.push(y);
}
}
return true;
};
e[n].emplace_back(0, -k);
e[0].emplace_back(n, k);
if(bellman_ford()) {
for(int i = 1, x; i <= n; i ++) {
x = dis[i] - dis[i - 1];
cout << "LDW"[x + 1];
}
return 0;
}
e[n].pop_back(), e[n].emplace_back(0, k);
e[0].pop_back(), e[0].emplace_back(n, -k);
if(bellman_ford()) {
for(int i = 1, x; i <= n; i ++) {
x = dis[i] - dis[i - 1];
cout << "LDW"[x + 1];
}
return 0;
}
cout << "NO";
return 0;
}
DP
记W
表示\(1\),L
表示\(-1\),D
表示\(0\)。
设状态\(f[i][j]\)表示前\(i\)位和为\(j\)的是否成立。如果是问号的话我们就枚举当前位的状态。转移过程中记录一下前序状态即可。
#include <bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
#define int i64
using vi = vector<int>;
using pii = pair<int,int>;
const i64 inf = LLONG_MAX / 2;
i32 main(){
ios::sync_with_stdio(false), cin.tie(nullptr);
int n, k;
cin >> n >> k;
string s;
cin >> s;
s = " " + s;
vector<map<int,bool>> f(n + 1);
vector<map<int,int>> lst(n + 1);
f[0][0] = true;
for(int i = 1, K; i <= n; i ++) {
K = k - (i != n);
if(s[i] == '?') {
for(int d = -1; d <= 1; d ++) {
for(int j = -K; j <= K; j ++)
if(f[i - 1][j - d]) f[i][j] = true, lst[i][j] = j - d;
}
} else {
int d = 0;
if(s[i] == 'W') d = 1;
else if(s[i] == 'L') d = -1;
for(int j = -K, l; j <= K; j ++)
if(f[i - 1][j - d]) f[i][j] = true, lst[i][j] = j - d;
}
}
if(f[n][k]) {
string res;
for(int i = n, j = k, l, x; i > 0; i --) {
l = lst[i][j], x = j - l;
res += "LDW"[x + 1];
j = l;
}
ranges::reverse(res);
cout << res;
}else if(f[n][-k]) {
string res;
for(int i = n, j = -k, l, x; i > 0; i --) {
l = lst[i][j], x = j - l;
res += "LDW"[x + 1];
j = l;
}
ranges::reverse(res);
cout << res;
} else {
cout << "NO";
}
return 0;
}