2022.11.22
2022.11.22
很不对劲。
YCC 个大 SB。
感觉状态远不如在日照的时候。
YCC 种草世界杯?
明明啥也看不懂,但是还很想看。
装箱
能很显然的看出是个 dp。
/*
Date:2022.11.22
Source:模拟赛
knowledge: f[i] 表示前i个的最小花费
*/
#include <cstdio>
#include <iostream>
#include <cstring>
#define orz cout << "AK IOI" << "\n";
#define int long long
using namespace std;
const int maxn = 2e4 + 10;
inline int read()
{
int x = 0, f = 1; char ch = getchar();
while(ch > '9' || ch < '0') {if(ch == '-') f = -1; ch = getchar();}
while(ch <= '9' && ch >= '0') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
inline void print(int X)
{
if(X < 0) X = ~(X - 1), putchar('-');
if(X > 9) print(X / 10);
putchar(X % 10 ^ '0');
return ;
}
inline int Max(int a, int b){
return a > b ? a : b;
}
inline int Min(int a, int b){
return a < b ? a : b;
}
int n, m, k, a[maxn], f[maxn];
signed main()
{
n = read(), m = read(), k = read();
for(int i = 1; i <= n; i++) f[i] = 1e18;
for(int i = 1; i <= n; i++) a[i] = read();
for(int i = 1; i <= n; i++)
{
int maxx = -1e18, minn = 1e18;
for (int j = i; j <= min(n, i + m - 1); j++)
{
maxx = Max(maxx, a[j]);
minn = Min(minn, a[j]);
f[j] = min(f[j], f[i - 1] + (j - i + 1) * (maxx - minn) + k);
}
}
print(f[n]);
return 0;
}
二进制
30pts 爆搜
/*
Date:
Source:
knowledge:
*/
#include <cstdio>
#include <iostream>
#define orz cout << "AK IOI" << "\n";
#define int long long
using namespace std;
const int maxn = 1e6 + 10;
inline int read()
{
int x = 0, f = 1; char ch = getchar();
while(ch > '9' || ch < '0') {if(ch == '-') f = -1; ch = getchar();}
while(ch <= '9' && ch >= '0') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
inline void print(int X)
{
if(X < 0) X = ~(X - 1), putchar('-');
if(X > 9) print(X / 10);
putchar(X % 10 ^ '0');
return ;
}
inline int Max(int a, int b){
return a > b ? a : b;
}
inline int Min(int a, int b){
return a < b ? a : b;
}
int n;
char a[maxn];
namespace sub1{
int power[10], ans = 0x3f3f3f3f, num;
void init()
{
power[0] = 1;
for(int i = 1; i <= 10; i++) power[i] = power[i - 1] * 2;
}
void dfs(int tot, int now, int res)
{
if(now == n)
{
if(tot == num) ans = min(ans, res);
return ;
}
dfs(tot + power[now], now + 1, res + 1);
dfs(tot - power[now], now + 1, res + 1);
dfs(tot, now + 1, res);
}
void main()
{
init();
num = 0;
for(int i = 1; i <= n; i++) if((int)a[i] == 49) num = num + power[n - i];
dfs(0, 0, 0);
print(ans);
}
}
signed main()
{
//freopen("twobit.in", "r", stdin);
//freopen("twobit.out", "w", stdout);
n = read();
cin >> a + 1; sub1::main();
//fclose(stdin);
//fclose(stdout);
return 0;
}
简化一下题意只有两种操作,花费 1 翻转一位, 花费 2 反转一段。
发现其中的性质。
连续两个 0 会使答案前后分别考虑
类似10101010 的情况用操作 1 更优,
其他情况运用操作 2 然后对 0 的操作位置进行操作 1 会更优。
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<cmath>
#define M 1000100
#define ll long long
using namespace std;
int read() {
int nm = 0, f = 1;
char c = getchar();
for(; !isdigit(c); c = getchar()) if(c == '-') f = -1;
for(; isdigit(c); c = getchar()) nm = nm * 10 + c - '0';
return nm * f;
}
char s[M];
int a[M], sum[M], n, f[M], sta[M], tp;
int main() {
freopen("twobit.in", "r", stdin), freopen("twobit.out", "w", stdout);
n = read();
scanf("%s", s + 1);
for(int i = 1; i <= n; i++) a[i] = s[n - i + 1] - '0', sum[i] = sum[i - 1] + (a[i] == 0);
if(n <= 300) {
f[0] = 0;
for(int i = 1; i <= n; i++) {
f[i] = f[i - 1] + a[i];
for(int j = 1; j < i; j++) f[i] = min(f[i], f[j - 1] + 2 + sum[i] - sum[j - 1]);
}
cout << f[n] << "\n";
} else {
int ans = n - sum[n], tmp = 0;
int now = 1;
while(1) {
while(!a[now] && now <= n) now++;
tp = 0;
if(now > n) break;
while(a[now] || a[now + 1]) sta[++tp] = now++;
tmp += min(tp - (sum[sta[tp]] - sum[sta[1] - 1]), 2 + sum[sta[tp]] - sum[sta[1] - 1]);
}
ans = min(ans, tmp);
cout << ans << "\n";
}
return 0;
}
摆!