https://ac.nowcoder.com/acm/contest/6885

D - 牛妹爱数列

 

题意:给定一个01序列,两种操作:翻转某一位;翻转第一位到某一位所有位。问最少几次操作使得变为全 0.

dp

做法一:翻转某一个区间需要 2 次操作。考虑枚举翻转的块的大小,则需要枚举所有 1 到 i-1,需要再化简dp式子,用一个 mn 记录前面的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int INF = 0x3f3f3f3f;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const int N = 2e6 + 10;
const ll mod = 1e9 + 7;
int n;
int dp[N], a[N], s[N];
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++)scanf("%d", &a[i]), s[i] = s[i - 1] + a[i];
dp[1] = a[1];
int mn = dp[1] + s[1] - 1;
for (int i = 2; i <= n; i++) {
dp[i] = mn + i - s[i] + 2;
dp[i] = min(dp[i], s[i]);
dp[i] = min(dp[i], i - s[i] + 1);
dp[i] = min(dp[i], dp[i - 1] + a[i]);
mn = min(mn, dp[i] + s[i] - i);
}
printf("%d\n", dp[n]);
return 0;
}

 

做法二:题解做法。

dp[i][0/1]dp[i][0/1] 表示把前 i 位全变成 0/1 的最少操作次数。

如果当前位置上a[i]=1a[i]=1,则:

dp[i][0]=min(dp[i1][0]+1,dp[i1][1]+1)dp[i][0]=min(dp[i - 1][0] + 1, dp[i - 1][1] + 1),我们可以将a[i]a[i]单独翻转,也可以将这前缀1一起翻转;

dp[i][1]=min(dp[i1][1],dp[i1][0]+1)dp[i][1] = min(dp[i - 1][1], dp[i - 1][0] + 1),我们可以将前缀0翻转,然后把a[i]a[i]拼上去。

如果当前位置上a[i]=0a[i]=0 类似。

1
2
3
4
5
6
7
8
9
10
rep(i, 1, n) {
if (a[i] == 1) {
dp[i][0] = min(dp[i - 1][0] + 1, dp[i - 1][1] + 1);
dp[i][1] = min(dp[i - 1][1], dp[i - 1][0] + 1);
} else {
dp[i][0] = min(dp[i - 1][0], dp[i - 1][1] + 1);
dp[i][1] = min(dp[i - 1][0] + 1, dp[i - 1][1] + 1);
}
}
printf("%d\n", dp[n][0]);

 

E - 牛妹游历城市

 

题意:给定 n 个数,两个数如果二进制并不为 0,则有连边,边权为二进制并的lowbit,问从第 1 个点到第 n 个点的最短路径长度。1n105,1ai<2321≤n≤10^5,1≤a^i<2^{32}

二进制+最短路

点很多,直接求肯定超时,但是既然是二进制,那么可以试着把点转化为边,把二进制作为点。

原本的点仍然保留,再对每个二进制位各建一个点,对于每个数,把它原本的点与该数为 1 的二进制位对应的新点连接,新图不带边权,带点权,旧点点权为 0,新点点权为 对应二进制大小。

则两个旧点需要联通必定要经过一些二进制点,为了走最短路,必定走的是两个数的lowbit。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int INF = 0x3f3f3f3f;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const int N = 2e6 + 10;
const ll mod = 1e9 + 7;
int T;
int n;
ll w[N], a[N];
vector<int>G[N];
typedef pair<ll, int>pii;
ll d[N];
ll bfs() {
priority_queue<pii, vector<pii>, greater<pii> >q;
memset(d, 0x3f, sizeof(d));
d[1] = 0;
q.push(pii(0, 1));
while (!q.empty()) {
ll di = q.top().first; int u = q.top().second; q.pop();
if (di > d[u])continue;
if (u == n)return di;
for (int v : G[u]) {
if (d[v] > d[u] + w[v]) {
d[v] = d[u] + w[v];
q.push(pii(d[v], v));
}
}
}
return -1;
}
int main() {
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
for (int i = 1; i <= n + 32; i++)G[i].clear();
fill(w, w + n + 33, 0);
for (int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
for (int j = 1; j <= 32; j++) {
if (a[i] >> (j - 1) & 1) {
G[i].push_back(j + n);
G[j + n].push_back(i);
}
}
}
for (int i = 1; i <= 32; i++)w[i + n] = (1ll << (i - 1));
ll ans = bfs();
if (ans == -1)puts("Impossible");
else printf("%lld\n", ans);
}
return 0;
}