#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
int a[4];
int main(void) {
int n; cin >> n;
int mn1, mx1, mn2, mx2, mn3, mx3;
cin >> mn1 >> mx1;
cin >> mn2 >> mx2;
cin >> mn3 >> mx3;
a[3] = mn3; a[2] = mn2;
int res = n - a[2] - a[3];
if (res <= mx1) a[1] = res;
else {
a[1] = mx1; res -= a[1];
if (a[2] + res <= mx2) a[2] += res;
else {
a[2] = mx2; a[3] += (res - (a[2] - mn2));
}
}
cout << a[1] << " " << a[2] << " " << a[3] << endl;
return 0;
}
贪心 || 二分 B - Pasha and Tea
题意:有n个girl和n个boy喝茶,茶杯的容量不等,boy喝的是girl的两倍且boy喝的一样多,girl喝的一样多,问主人最多能倒出多少水
分析:第一反应是用二分搜索girl喝的茶容量,可惜写搓了,mid应该和茶量最小的girl以及boy去比较。看过的代码竟然99%都是贪心,晕~
代码(二分):
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
const int INF = 0x3f3f3f3f;
const double EPS = 1e-9;
int a[N*2];
int main(void) {
int n; int w; scanf ("%d%d", &n, &w);
n *= 2;
for (int i=1; i<=n; ++i) {
scanf ("%d", &a[i]);
}
sort (a+1, a+1+n);
double l = 0.0, r = w;
double mid = 0.0, ans = 0.0;
for (int i=1; i<=100; ++i) {
mid = (l + r) / 2.0;
if (mid >= 0 && mid <= a[1] && mid * 2 <= a[n/2+1] && mid * 1.5 * n <= w) {
l = mid;
ans = mid * 1.5 * n;
}
else r = mid;
}
printf ("%.6f
", ans);
return 0;
}
代码(贪心):
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int a[N*2];
int main(void) {
int n, w; scanf ("%d%d", &n, &w);
n *= 2;
for (int i=1; i<=n; ++i) scanf ("%d", &a[i]);
sort (a+1, a+1+n);
double ans = min (a[1] * 1.0, a[n/2+1] / 2.0);
ans = min (ans * 3 * n / 2.0, w * 1.0);
printf ("%.6f
", ans);
return 0;
}
构造+贪心:C - Arthur and Table
题意:简单说就是拿掉一些凳脚,使得剩下的最长的凳脚的个数比一半还多,每条凳子有拿走的费用,问最小费用是多少。
分析:先按照凳长排序,从最大的开始枚举,相同长度的跳过,那么统计还需要拿掉几条凳脚,优先拿费用小的,怎么找呢?这题很人性的将费用范围定在200内,直接暴力找啊!
收获:算不上收获,这道题不需要什么算法,脑子清晰地实现代码,构造题要思维严谨,逻辑清楚
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
const int INF = 0x3f3f3f3f;
struct Leg {
int l, cnt, d;
bool operator < (const Leg &r) const {
return l < r.l;
}
}leg[N];
int c[222];
int main(void) {
int n; scanf ("%d", &n);
for (int i=1; i<=n; ++i) {
scanf ("%d", &leg[i].l);
}
memset (c, 0, sizeof (c));
int tot = 0;
for (int i=1; i<=n; ++i) {
scanf ("%d", &leg[i].d);
tot += leg[i].d; c[leg[i].d]++;
}
sort (leg+1, leg+1+n);
int i = n, ans = INF, sum = 0;
while (i >= 1) {
int l, r = i;
while (i > 1 && leg[i].l == leg[i-1].l) {
c[leg[i].d]--; i--;
}
c[leg[i].d]--; l = i;
int num = r - l + 1;
if (num * 2 -1 >= r) ans = min (ans, sum);
else if (num == 1) ans = min (ans, tot - leg[i].d);
else {
int res = r - (num * 2 - 1);
int cost = 0;
for (int j=0; j<=200 && res > 0; ++j) {
if (c[j]) {
int t = min (res, c[j]);
res -= t;
cost += t * j;
}
}
ans = min (ans, sum + cost);
}
i--;
for (int j=l; j<=r; ++j) sum += leg[j].d;
}
printf ("%d
", ans);
return 0;
}
UPD:
字典树+区间DP/Hash+DFS E - Ann and Half-Palindrome
题意:一个长度n的字符串(n<=5000),仅含有a和b,定义半回文串:满足当i是奇数位置,且str[i]=str[n-1-i](i<=(n+1)/2)的串,求所有子串按照字典序后第k个字串。
思路:没注意到复杂度是可以接受的。首先区间DP判断子串[i,j]是否为半回文串,然后将所有子串插入字典树上,插入的过程中标记是半回文串的点,然后DFS中序遍历,找第k个。
#include <bits/stdc++.h>
const int N = 5e3 + 5;
const int NODE = N * (N+1) / 2;
int ch[NODE][2];
int val[NODE];
int sz;
char str[N];
bool dp[N][N];
int n, k;
void trie_init() {
//memset (ch[0], 0, sizeof (ch[0]));
//val[0] = 0;
sz = 1;
}
void trie_insert(char *s, int start) {
int u = 0, c;
for (int i=start; i<n; ++i) {
c = s[i]-'a';
if (!ch[u][c]) {
memset (ch[sz], 0, sizeof (ch[sz]));
val[sz] = 0;
ch[u][c] = sz++;
}
u = ch[u][c];
if (dp[start][i]) val[u]++;
}
}
char path[N];
int m;
bool trie_DFS(int u) {
k -= val[u];
if (k <= 0) return true; //printf, exit (0);
for (int i=0; i<2; ++i) {
if (ch[u][i]) {
path[m++] = 'a' + i; path[m] = ' ';
if (trie_DFS (ch[u][i])) return true;
path[--m] = ' ';
}
}
return false;
}
void solve(char *s) {
n = strlen (s);
for (int i=0; i<n; ++i) {
for (int j=i; j>=0; --j) {
if (i - j <= 3) {
dp[j][i] = (s[j] == s[i]);
} else {
dp[j][i] = (s[j] == s[i]) && dp[j+2][i-2];
}
}
}
trie_init ();
for (int i=0; i<n; ++i) {
trie_insert (s, i);
}
m = 0;
trie_DFS (0);
printf ("%s
", path);
}
int main() {
scanf ("%s%d", str, &k);
solve (str);
return 0;
}
再附上铭神的hash+指针字典树代码
#include <bits/stdc++.h>
using ull = unsigned long long;
const int N = 5e3 + 5;
const ull base = 233;
ull base_pow[N];
ull lhash[N], rhash[N];
char str[N];
int n, k;
ull get_hash(ull *hash, int p, int len) {
if (len & 1) len++;
return hash[p] - hash[p+len] * base_pow[len/2];
}
struct Node {
Node *go[2];
int cnt;
};
Node pool[N*N], *alloc, *root;
void insert(int p) {
Node *u = root;
for (int i=0; p+i<n; ++i) {
int c = str[p+i] - 'a';
if (u->go[c] == NULL) {
u->go[c] = alloc++;
}
u = u->go[c];
int len = i + 1;
if (get_hash (lhash, p, len+1>>1) == get_hash (rhash, n-1-(p+i), len+1>>1)) {
u->cnt++;
}
}
}
std::vector<int> path;
bool DFS(Node *u) {
if (u == NULL) return false;
k -= u->cnt;
if (k <= 0) return true;
path.push_back(0);
if (DFS(u->go[0])) return true;
path.back() = 1;
if (DFS(u->go[1])) return true;
path.pop_back();
return false;
}
int main() {
scanf ("%s%d", str, &k);
n = strlen (str);
base_pow[0] = 1;
for (int i=1; i<N; ++i) {
base_pow[i] = base_pow[i-1] * base;
}
for (int i=n-1; i>=0; --i) {
lhash[i] = lhash[i+2] * base + str[i];
rhash[i] = rhash[i+2] * base + str[n-1-i];
}
alloc = pool;
root = alloc++;
for (int i=0; i<n; ++i) {
insert (i);
}
DFS (root);
for (auto t: path) {
putchar ('a' + t);
}
puts ("");
return 0;
}
未完待续~