Codeforces Round #494(Div.3)
A. Polycarp's Pockets
题意:
- 一个人有n个硬币,面值为\(a_i\),他打算把他分到几个口袋当中。
- 每个口袋中不可以有相同的硬币。
- 问最少需要多少口袋。
- 数据范围:\(a_i\leq 100\)。
思路:
- 求这n个序列中出现次数最多的硬币即可。
#include
using namespace std;
int n, vis[100+10];
int main()
{
cin >> n;
for(int i = 1, x; i <= n; i++)
{
scanf("%d", &x);
vis[x]++;
} int ans = 0;
for(int i = 1; i <= 100+2; i++)
ans = max(ans, vis[i]);
cout << ans << endl;
return 0;
}
B. Binary String Constructing
题意:
- 你有三个数字\(a,b,x\)。
- 你需要构造一个字符串长度为\(n=a+b\),字符串中有\(a\)个\(0\),\(b\)个\(1\)。
- 定义特殊对为:如果两个相邻的位置字符不同,则为一个特殊对。
- 要求构造的字符串也要有\(x\)个特殊对。
思路:
- 可以先输出\(\frac{x}{2}\)对0和1,那么这样就能提供\(\frac{x}{2}*2-1\)个符合题目的特殊对。
- 之后将剩余的0和剩余的1连续输出就行。
- 当然还是要卡一些细节的。
- 同时还要注意a和b的大小,因为如果0的数目更多一些,最好把0放在前面,因为如果把1放在前面的话,1可能不够用。
#include
using namespace std;
int main()
{
int a, b, x;
cin >> a >> b >> x;
string ans;
char u, v;
if(a > b) u = '0', v = '1';
else u = '1', v = '0', swap(a, b);
for(int i = 1; i <= x/2; i++)
ans += u, ans += v, a--, b--;
if(x % 2 == 0)
{
while(b--) ans += v;
while(a--) ans += u;
}
else
{
while(a--) ans += u;
while(b--) ans += v;
}
cout << ans << endl;
}
C. Intense Heat
题意:
- 求长度大于等于k的连续子段的平均值的最大值。
- 数据范围:\(n\leq 5000\)
思路:
- 暴力枚举序列长度从k到n。
- 同时枚举区间的右端点,利用前缀和\(O(1)\)求值后计算。
#include
using namespace std;
const int maxn = 5e3 + 10;
double n, k, a[maxn];
//输出15位小数
int main()
{
double ans = 0;
cin >> n >> k;
for(int i = 1; i <= n; i++)
{
cin >> a[i];
a[i] = a[i-1] + a[i];
}
//枚举长度
for(int i = k; i <= n; i++)
//枚举右端点
for(int j = i; j <= n; j++)
ans = max(ans, (a[j]-a[j-i])/i);
printf("%.15f\n", ans);
return 0;
}
D. Coins and Queries
题意:
- 有n枚硬币,每一枚硬币价值为\(a_i\),所有价值都是2的整数次幂。
- 有q次询问,每次给出一个数字问是否能从这n枚硬币的子集中选出一个来组成这个面值的数字。
- 求这个子集最小是多少,如果无法得到这个面值,输出-1。
思路:
- 我们知道任何一个数字都可以表示成二进制的形式,所以可以从这个方向下手。
- 因为硬币是2的整数次幂,所以我们先预处理出来每个位置上的数目,然后每当我们要凑钱的时候就从高位往低位配。
- 算是贪心。
#include
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 10;
int n, q;
int sum[35];
int main()
{
scanf("%d%d", &n, &q);
for(int i = 1, x; i <= n; i++)
{
scanf("%d", &x);
int t = log2(x);
sum[t]++;
}
while(q--)
{
int x, ans = 0;
scanf("%d", &x);
for(int i = 31; i >= 0; i--)
{
int cur = min(sum[i], x/(1<
E. Tree Constructing
题意:
- 给定\(n,d,k\),构造一颗有n个节点,直径为d,点的度数最多为k的树。
思路:
- 直接构造一条长度为d的长链,然后在树上的点上插入叶子就行。
- 当然也要注意插入规则,两端的点不能插入,两端靠中间挪动一个点的地方可以插入一个深度为1的子树,再往中间挪动可以插入深度为2的子树,以此类推。
- 感觉这道构造题还是挺考验码力的。开始写的是dfs结果MLE了也不知道为啥,改成了bfs。
#include
using namespace std;
int n, d, k;
int cnt;
vector> ans;
void bfs(int depth, int fa, int deg)
{
if(depth <= 0) return;
if(cnt == n+1) return;
queue> q;
for(int i = 1; i <= deg; i++)
{
ans.push_back({fa, cnt});
q.push({cnt, 1});
cnt++;
if(cnt == n+1) return;
}
depth--;//已经用了一层了
while(q.size())
{
int x = q.front().first, d = q.front().second;
if(d == depth+1) return; q.pop();
for(int i = 1; i <= deg+1; i++)
{
ans.push_back({x, cnt});
q.push({cnt, d+1});
cnt++;
if(cnt == n+1) return;
}
}
}
int main()
{
scanf("%d%d%d", &n, &d, &k);
cnt = 1;
if(d >= n || (d != k && k == 1))
{
puts("NO");
return 0;
}
//先直接构造出链条
for(int i = 1; i <= d; i++)
ans.push_back({cnt, cnt+1}), cnt++;
//往枝杈上插入点
cnt++;
for(int i = 2, j = d; i <= j; i++, j--)
{
int depth = i - 1; //当前这个点能插入深度多少的叶子
bfs(depth, i, k-2);
if(i != j) bfs(depth, j, k-2);
if(cnt == n+1) break;
}
if(cnt != n+1) puts("NO");
else
{
puts("YES");
for(auto x : ans)
printf("%d %d\n", x.first, x.second);
}
return 0;
}
F. Abbreviation
题意:
- 给几个字符串,有一定规则能够缩短这个字符串。
- 问进行一次缩短操作后字符串最短长度为多少(算上空格)
- 数据范围:\(n\leq 300\)
思路:
我看官方题解写的。
设\(f(i,j)\)表示以\(i,j\)开始最长能够匹配的字符串。
- 枚举所有起点和区间长度,计算结果。
- 注释写的比较详细了。
官方题解
#include
using namespace std;
const int maxn = 300 + 10;
int f[maxn][maxn];
int n;
int len;
vector a;
int main()
{
scanf("%d", &n);
int len = n-1;
for(int i = 0; i < n; i++)
{
string str; cin >> str;
a.push_back(str);
len += str.size();
}
for(int i = n - 1; i >= 0; i--)
for(int j = n-1; j >= 0; j--)
{
if(a[i] == a[j])
if(i + 1 < n && j + 1 < n)
f[i][j] = f[i+1][j+1]+1;
else f[i][j] = 1;
}
int ans = len;
for(int i = 0; i < n; i++) //枚举开头
{
int sum = 0;
for(int j = 0; i + j < n; j++) //枚举长度
{
sum += a[i+j].size(); //长度为j时的长度和
int cnt = 1;
for(int pos = i+j+1; pos < n; pos++)
if(f[i][pos] > j) //相同 则可以简化
{
++cnt; //相同的串加一
pos += j; //跳过这个区间
}
//当前计算的结果 = 总长度 - 长度为j时的长度*有多少个这样的区间 + 区间数
int cur = len - sum*cnt + cnt;
if(cnt > 1 && ans > cur) ans = cur;
}
} cout << ans << endl;
return 0;
}