个人思路,仅作记录,可以参考,欢迎交流。
比赛地址:传送门
(太忙所以没打,结果赛后随便做了一下前四题发现居然很简单,亏了)
A. Red Versus Blue
传送门
【题意】求一个只含r个’R’和b个’B’(r>b),且「最长同字符子串」最短的字符串。
【思路】将r个’R’尽可能平均地分为b+1份,每一份用’B’隔开。
【代码】
void solve()
{
int n, r, b;
cin >> n >> r >> b;
string ans;
for (int i = 1; i <= r % (b + 1); ++i)
{
if (i > 1)
{
ans += 'B';
}
for (int j = 1; j <= r / (b + 1) + 1; ++j)
{
ans += 'R';
}
}
for (int i = 1; i <= (b + 1) - r % (b + 1); ++i)
{
if (ans.length())
{
ans += 'B';
}
for (int j = 1; j <= r / (b + 1); ++j)
{
ans += 'R';
}
}
cout << ans << '\n';
return;
}
B. Bit Flipping
传送门
【题意】给定一个二进制串和一种 *** 作:
- 选定二进制串中的一位,将该位以外的其他位取反
求如何对二进制串进行k次 *** 作能得到对应数值最大的串,并求这个最大串。
【思路】若对第i位进行m次 *** 作,则第i位会取反k-m次。所以第i位最后是否取反只与k和m的奇偶有关,只要对k的奇偶进行分类讨论,然后从高位向低位考虑,尽量在给每个0分配最少 *** 作次数(分配1或0)的情况下使其取反为1。最后剩余的 *** 作次数若为偶数,则分配给任一位都不影响答案;而若为奇数,则可以分配给最后一位使其从1变为0,因为这样对答案的削减最小。
【代码】
int ans[200005];
void solve()
{
int n, k;
int o = 0, e = 0;
string str;
cin >> n >> k;
cin >> str;
int quota = k;
for (int i = 0; i < n; ++i)
{
if (k % 2)
{
if (str[i] == '1')
{
if (quota)
{
quota--;
ans[i] = 1;
}
else
{
str[i] = '0';
ans[i] = 0;
}
}
else if (str[i] == '0')
{
ans[i] = 0;
str[i] = '1';
}
}
else//k偶
{
if (str[i] == '1')
{
ans[i] = 0;
}
else if (str[i] == '0')
{
if (quota)
{
quota--;
ans[i] = 1;
str[i] = '1';
}
else
{
ans[i] = 0;
}
}
}
}
if (quota)
{
ans[n - 1] += quota;
if (quota % 2)
{
str[n - 1] = (str[n - 1] == '1' ? '0' : '1');
}
}
cout << str << '\n';
for (int i = 0; i < n; ++i)
{
cout << (i ? " " : "") << ans[i];
}
cout << '\n';
return;
}
C. Line Empire
传送门
【题意】给定n座城到城0的距离,以及两种 *** 作:
- 1. 在首都为i的情况下将首都向右迁到城j,花费a*(d[j]-d[i])
- 2. 在首都为i的情况下占领右方第一个未被占领的城j,花费b*(d[j]-d[i])
初始时只占有0且其为首都。求最少花费多少才能占领所有的城。
【思路】对于同一起始地的迁都 *** 作,早迁一定优于晚迁。所以迁都的目的地只考虑最新占领的城。当已经占领到城j且首都为i时,将首都迁至j的花费为a*(d[j]-d[i]),而可以为后续占领节省的花费为b*(n-j)*(d[j]-d[i])。令a*(d[j]-d[i])⌈a/b⌉时选择迁都至j,其余情况不迁,直接占领。
【代码】
long long x[200005] = { 0 };
void solve()
{
long long n, a, b;
cin >> n >> a >> b;
for (int i = 1; i <= n; ++i)
{
cin >> x[i];
}
if (n == 1)//注意特殊情况
{
cout << b * x[1] << '\n';
}
else
{
long long ans = 0;
long long move = n - min(n, (a / b + !!(a % b)));//注意特殊情况
ans += a * x[move];
ans += b * x[move];
for (long long i = move + 1; i <= n; ++i)
{
ans += b * (x[i] - x[move]);
}
cout << ans << '\n';
}
return;
}
D. Reverse Sort Sum
传送门
【题意】给定一个长度为n的二进制串,由如下方法衍生出n个二进制串:第i个衍生串为将原串前i位进行从小到大排序后的结果(i=1,2,…,n)。对于每一位,已知所有衍生串上该位的值的和,求任一满足要求的原串。
【思路】观察规律。
做法为:从最后一位开始考虑,将每一位的总和减掉该位蓝色部分总和就能判断出原串该位是0还是1。下面说明如何得到每一位蓝色部分的总和。
首先,根据所有位的总和可以计算出原串中1的个数sum,则倒数前sum列的蓝色部分一定都为1。
考虑特殊情况:若原串中1都分布在右方,则蓝色部分只有倒数前i位为1,其余都为0。
在此基础上,一旦判断出原串中某一位为0,分配给蓝色部分的1就会增加。在图上体现为:这一列0会将它们左边的蓝色部分的1向左“推”(想一想,为什么?)。所以,当某一位的蓝色部分不全为0时,其内0的个数与原串中0的出现位数有关(关系如图)。
所以只需每次判断出0时记下0的位置信息,遇到蓝色部分有0的位时消耗最早记下的信息。如果发现已经消耗完,说明之后所有位的蓝色部分一定都为0,也即原串的之后所有位都为0。
具体见代码及注释。
【代码】
int c[200005];
int a[200005];
queue q;
void solve()
{
while (q.empty() == 0)
{
q.pop();
}
int n;
cin >> n;
long long sum = 0;
for (int i = 1; i <= n; ++i)
{
cin >> c[i];
sum += c[i];
}
sum /= n;//原串中1的个数
for (int i = n; i >= 2; --i)
{
if (n - i < sum)//第i位蓝色部分全为1
{
a[i] = c[i] - (n - i + 1);
if (a[i])//第i位为1
{
a[i] /= i - 1;
}
else//第i位为0
{
q.push(n - i + 1);
}
}
else//第i位蓝色部分有0
{
if (q.empty() == 0)//消耗之前记录的信息
{
a[i] = c[i] + q.front() - (n - i + 1);
q.pop();
if (a[i])//第i位为1
{
a[i] /= i - 1;
}
else//第i位为0
{
q.push(n - i + 1);
}
}
else//原串之后的位已经不可能再有1了
{
a[i] = 0;
}
}
}
a[1] = !!c[1];//第一位比较特殊,特判一下
for (int i = 1; i <= n; ++i)
{
cout << a[i] << (i == n ? "\n" : " ");
}
return;
}
(好累好烦,这枯燥的学习生活里只有写代码能给我带来一丝快乐了)
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)