A.炸鸡块哥哥的粉丝题
输出字符串的前 ⌈ n 2 ⌉ \lceil \frac{n}{2} \rceil ⌈2n⌉ 个字符
void solve()
{
int n;
string s;
cin >> n >> s;
cout << s.substr(0, (n + 1) / 2);
}
B.智乃想考一道鸽巢原理
当小球总个数为奇数时,贪心的留下 1 个小球,反之,留下 2 个小球,我们将这个值定义为 P ,即该颜色小球最小留下的个数。
常见的此类问题,我们只需要得到最大值,之后判断
t
o
t
≤
M
a
x
{
a
i
}
×
2
tot\le Max\{a_i\} \times 2
tot≤Max{ai}×2 即可判断这个球是否会留下,但是这个题要求判断每一个小球是否能留下,要解决这个问题需要额外记录一个次大值即可解决。
void solve()
{
ll n, mx = 0, sx = 0, tot = 0;
ll p=1;
cin >> n;
vector<ll> a(n);
for (ll &i : a)
{
cin >> i;
tot += i;
if (i >= mx)
{
sx = max(sx, mx);
mx = i;
}
else
sx = max(i, sx);
}
if(tot%2==0)
p=2;
for (int i = 0; i < n; i++)
{
if(a[i]<p)
cout<< "0";
else if ((a[i] == mx && (tot - p) >= sx * 2) || (tot - p) >= mx * 2)
cout << "1";
else
cout << "0";
cout << " \n"[i == n - 1];
}
}
C.智乃想考一道完全背包(Easy version)
首先我们注意到题目的要求
a
1
≤
a
2
≤
.
.
.
≤
a
k
≥
a
k
+
1
≥
.
.
.
≥
a
n
a_1 \le a_2\le...\le a_k\ge a_{k+1} \ge ... \ge a_n
a1≤a2≤...≤ak≥ak+1≥...≥an
由这个限制条件我们很容易想到的是,当我们选取一个 K 以左的物品
a
i
a_i
ai 时,我们必须保证
i
→
k
i \to k
i→k 这一段物品依次选取过一次,那么我们就可以对背包物品的体积和价值预处理一下。
for (int i = k - 1; i >= 1; i--)
{
w[i] += w[i + 1];
v[i] += v[i + 1];
}
for (int i = k + 1; i <= n; i++)
{
w[i] += w[i - 1];
v[i] += v[i - 1];
}
然而当我们这样处理时,会导致第 K 个物品可能出现多余取的情况,如下图
为了解决这种情况,我们把这 N 个已被组合的区间互相组合一下,易证,最多会有 62500 个物品存在(
M
≤
500
M \le 500
M≤500,假定每个物品体积均为 1,最多左右 250 个物品直之间合并),之后对这些物品跑一遍完全背包,时间复杂度为
O
(
m
3
4
)
O(\frac{m^3}{4})
O(4m3)
void solve()
{
ll k, n, m;
cin >> n >> m >> k;
vector<ll> v(n + 1), w(n + 1), f(m + 10);
for (int i = 1; i <= n; i++)
cin >> w[i] >> v[i];
for (int i = k - 1; i >= 1; i--)
{
w[i] += w[i + 1];
v[i] += v[i + 1];
}
for (int i = k + 1; i <= n; i++)
{
w[i] += w[i - 1];
v[i] += v[i - 1];
}
for (int i = 1; i <= k - 1; i++)
{
for (int j = k + 1; j <= n; j++)
{
if (w[i] + w[j] - w[k] <= m)
{
w.push_back(w[i] + w[j] - w[k]);
v.push_back(v[i] + v[j] - v[k]);
}
}
}
for (ll i = 1; i < v.size(); i++)
{
for (ll j = w[i]; j <= m; j++)
f[j] = max(f[j], f[j - w[i]] + v[i]);
}
for (int i = 1; i <= m; i++)
cout << f[i] << " \n"[i == m];
}