【A】给了一些水果,用这些水果来制作一种食物需要的比例是1:2:4,问制作最多的完整食物需要的水果数目是多少?
首先很容易确定的水果数目就是min(a, b/2, c/4),然后用这个数目乘以7就是我们要的答案了。
【B】通过对样例的观察,我们不难得到解决的方法,我们用两个指针,l,r对于n分奇偶来得到答案,通过观察知道,答案一定是分奇偶排列在上一次得到的序列的左右的,所以我们设置l,r一个较大值,然后最后对答案序列从l到r输出即可。
【C】一个人要从x1到x2,然后电车的初始位置是d,电车给了行驶的方向和车的初始位置,然后给了人和车的速度,最后问人从x1到达x2的最短时间。这个问题就是分类讨论,推公式的问题。再纸上画一下很容易推出来,就不写了。给出我的代码吧
int main()
{
int ans;
int s, x1, x2, t1, t2, p, d;
cin>>s>>x1>>x2>>t1>>t2>>p>>d;
if(x2 > x1 && d == 1){
if(p > x1) ans = min((x2-x1)*t2, (2*s+x2-p)*t1);
else ans = min((x2-x1)*t2, (x2 - p)*t1);
}
else if(x2 > x1 && d == -1){
ans = min((x2-x1)*t2, (p+x2)*t1);
}
else if(x2 < x1 && d == -1){
if(p < x1) ans = min((x1-x2)*t2, (2*s+p-x2)*t1);
else ans = min((x1-x2)*t2, (p-x2)*t1);
}
else{
ans = min((x1-x2)*t2, (2*s-x2-p)*t1);
}
cout<<ans<<endl;
}
【D】题目是要求构造一个序列,这个序列由a个'G'和b个‘B,并且任意连续的字符个数不能超过k个,其中a+b=n,问能否成功构造出这个序列,不能输出“NO”,否则输出你构造的序列。简单贪心,我们直接用k个a和k个b这样的序列,首字母是谁由a和b的大小来决定,我们一直这样怼下去,知道某种字母怼完之后,把另外一种字母直接放到后面就可以了。那么如果存在合法状态的话,我们得到的这个串肯定合法,否则我们只需要检查最后连续的相等字母的个数,判断是不是>k,就可以判断这个串是否合法了。
char s[100010];
int n, k, a, b, cnt;
int main()
{
cin>>n>>k>>a>>b;
int mx = max(a, b);
int mi = min(a, b);
int cha = mx - mi;
cnt = 0;
if(k > n){
printf("NO\n");
return 0;
}
if(mx == a){
for(int i = 0; i < min(k-1, cha); i++){
s[cnt++] = 'G';
}
cha -= min(k-1, cha);
if(mi){
s[cnt++] = 'G';
s[cnt++] = 'B';
mi--;
}
for(int i = 0; i < mi; i++){
for(int j = 0; j < min(k-1, cha); j++){
s[cnt++] = 'G';
}
cha-=min(k-1, cha);
s[cnt++] = 'G';
s[cnt++] = 'B';
}
while(cnt < n){
s[cnt] = 'G';
cnt++;
}
}
else{
for(int i = 0; i < min(k-1, cha); i++){
s[cnt++] = 'B';
}
cha -= min(k-1, cha);
if(mi){
s[cnt++] = 'B';
s[cnt++] = 'G';
mi--;
}
for(int i = 0; i < mi; i++){
for(int j = 0; j < min(k-1, cha); j++){
s[cnt++] = 'B';
}
cha-=min(k-1, cha);
s[cnt++] = 'B';
s[cnt++] = 'G';
}
while(cnt < n){
s[cnt] = 'B';
cnt++;
}
}
int cnt2 = 0;
for(int i = cnt - 1; i >= 0; i--){
if(s[i] == s[cnt-1]){
cnt2++;
}
else{
break;
}
}
//cout<<s<<endl;
if(cnt2 > k)
{
printf("NO\n");
}
else{
cout<<s<<endl;
}
}
【E】现在给了n个数字,然后还有一个1到m,代表可以把a里面任意一个数换成1-m,最后需要保证a数组里面的所有的数的奇数个数等于偶数个数,问最少的换的次数,以及换了之后的数组是什么样子的,当然还有不能达到的情况,输出“-1”。
const int maxn = 2e5+10;
int n, m, a[maxn], num[2], p[2]; //num记录奇偶的个数, p0, p1表示指针
bool flag[maxn]; //记录哪些改变过
set <int> s; //记录不用改变的
int main()
{
cin>>n>>m;
memset(flag, true, sizeof(flag));
for(int i = 1; i <= n; i++){
cin>>a[i];
if(s.count(a[i]) == 0){
num[a[i]&1]++;
s.insert(a[i]);
}
else{
flag[i] = false;
}
}
int mid = n / 2;
for(int j = 0; j < 2; j++){
if(num[j] > mid){
for(int i = 1; i <= n; i++){
if(flag[i] && ((a[i] & 1) == j)){
flag[i] = false;
num[j]--;
}
if(num[j] <= mid) break;
}
}
}
s.clear();
int ans = 0;
//把不需要改变的丢进来&&求出不需要改变的个数
for(int i = 1; i <= n; i++){
if(flag[i]){
s.insert(a[i]);
ans++;
}
}
p[0] = 2, p[1] = 1;
for(int j = 0; j < 2; j++){
if(num[j] < mid){
for(int i = 1; i <= n; i++){
if(flag[i]) continue;
while(s.count(p[j]) > 0) p[j] += 2;
if(p[j] > m){
puts("-1");
return 0;
}
num[j]++;
a[i] = p[j];
s.insert(a[i]);
flag[i] = true;
if(num[j] >= mid) break;
}
}
}
cout<<n-ans<<endl;
for(int i = 1; i <= n; i++) cout<<a[i]<<" ";
}
【F】一个人又k的时间听n首音乐,每一首音乐都有一个愉快度,然后这个人可以选择听这首音乐完整时间t,1/2向上取整的时间,还要满足听了一半的个数<=m。确实没什么想法:贴一个别人的关键解题方法吧:
a. 听的歌曲可以从任意位置开始
b. k时间内听得歌曲都必须听,不能跳过(而这点就限制了,解题思路不能往dp方向考虑,而是区间)
c. 最关键的是:这里固定区间,使得收益最大,(每首歌曲的收益都是正数,没有负数和0,而且听的时间减半也不会对收益造成影响),那么收益最大就是听尽可能多的歌曲,那么,很容易得出结论,(这个题目的限制条件,取半的歌曲的数目上限为w),寻找区间,使得区间内听歌时间最大的w个的歌曲取半,这样得到的是这个区间的最大收益值,这样结果很明确,不容考虑各种组合,不需要枚举,不需要dp,(反正这里也不能dp),最优的也就那么一种。
d. 最终的答案就是遍历所有可能这样区间即可,固定左边界,右边界是非递减的,这就可以使用two pointer来做!
看完之后才觉得恍然大悟,按照官方题解的思路,看着别人的代码写出来,太弱了。
int n, m, k, a[200010], b[200010];
int tt, res, now;
set <pair <int, int> > s1, s2;
int main()
{
cin >> n >> m >> k;
REP1(i, 0, n) scanf("%d", &a[i]); //please
REP1(i, 0, n) scanf("%d", &b[i]); //time
for(int l = 0, r = 0; r < n; ){
while(tt <= k && r < n){
if(s1.size() < m){
s1.insert(MP(b[r], r));
tt += (b[r] + 1) / 2;
}
else if(s1.begin()->first < b[r]){
tt -= (s1.begin() -> first + 1) / 2;
tt += s1.begin() -> first;
tt += (b[r] + 1) / 2;
s2.insert(*s1.begin());
s1.erase(*s1.begin());
s1.insert(MP(b[r], r));
}
else{
s2.insert(MP(b[r], r));
tt += b[r];
}
now += a[r++];
if(tt <= k) res = max(res, now);
}
while(tt > k)
{
if(s1.count(MP(b[l], l)) > 0){
tt -= (b[l] + 1) / 2;
s1.erase(MP(b[l], l));
if(!s2.empty()){
auto x = *s2.rbegin();
tt -= x.first;
tt += (x.first + 1) / 2;
s1.insert(x);
s2.erase(x);
}
}
else{
tt -= b[l];
s2.erase(MP(b[l], l));
}
now -= a[l++];
if(tt <= k){
res = max(res, now);
}
}
}
cout<<res<<endl;
}
【G】考虑一颗树,如果满足每层深度上有a[i]结点,最多能有多少叶子结点。那么答案很简单,就是对(a[i]-1)求和再加1(每一层的结点都集中在上一层的一个结点上)。同理,我们考虑最少能有多少叶子结点,就是把上一个的答案再减去min(a[i]-1, a[i-1]-1)的求和,就是每一层的结点都尽可能的分散在上一层的结点上,根据这个,那么如果要求有k个叶子节点,k在最大值与最小值之间,就可以生成出这棵树了。生成的方法,和构造起来差不多,就是先求出最大值与k的差T,然后每一层处理时,如果T>0,就使这一层尽可能的分散在上一层结点上,然后T减少一点。直到T为0,之后的层都直接集中在上一层的一个结点即可。
int max_leaf_node, min_leaf_node;
int n, t, k, a[maxn];
int main()
{
cin >> n >> t >> k;
REP2(i, 1, t) cin >> a[i];
max_leaf_node = 0;
REP1(i, 1, t) max_leaf_node += (a[i] - 1); max_leaf_node += a[t];
min_leaf_node = max_leaf_node;
REP2(i, 2, t) min_leaf_node -= min(a[i] - 1, a[i - 1] - 1);
if(k <= max_leaf_node && k >= min_leaf_node){
int cha_leaf_node = max_leaf_node - k;
cout << n << endl;
for(int i = 2; i <= 1 + a[1]; i++) cout << "1 " << i << endl;
int tot = 2 + a[1];
REP2(i, 2, t){
int t1 = min(a[i-1] - 1, a[i] - 1), t2 = 0;
while(cha_leaf_node > 0 && t1 > 0){
if(t2 > 0){
cha_leaf_node--;
t1--;
}
cout << tot - a[i - 1] + t2 << " " << tot + t2 << endl;
t2++;
}
if(cha_leaf_node == 0 || t1 == 0){
while(t2 < a[i]){
cout << tot - a[i - 1] << " " << tot + t2 << endl;
t2++;
}
}
tot += a[i];
}
}
else{
cout << -1 << endl;
}
}