杭二集训 dierti (CF GYM 100016F)

标签:贪心,DP

题目

题目传送门

2.1 backgrounds
小O是一个很萌很萌的女孩子
2.2 content
有一段时间,小O对保龄球十分感兴趣,她特地去研究了一下保龄球的 规则。 一场比赛一共有n轮, 在每轮的一开始有10个瓶子,每一轮小O有两次扔球机会。一轮结束 后,瓶子会被重置。 如果小O某一轮中击倒了10个瓶子,称为“奥义”。 特别的,如果小O第一次扔球就击倒了10个瓶子,称为“全给”(不再 称为“奥义”)。 另外,如果小O在第n轮中打出了“全给”,那么她可以获得额外的一 轮。(只能获得一次) 最后的分数是这么计算的: 每次扔球的基础分是该次击倒的瓶子数目。 如果上一次是“全给”,则小O额外获得与这轮基础分相同的奖励分。 如果上一次是“奥义”,则小O额外获得与这轮的第一次扔球的基础分 相同的奖励分。 最后的总分是把所有分加起来。 现在给出小O的比赛情况,她现在想知道,通过重新排列每轮的顺序能 获得的最大得分是多少。 提醒:有奖励轮时第n轮必须是“全给”,重排后也是一样。
2.3 input format
第一行一个正整数n,表示预定的轮数。 接下来n行,每行两个正整数ai,bi,表示小O第i轮两次扔球分别击倒 的瓶子个数。 特别的,如果an = 10,则还有一行输入,表示奖励轮中小O两次扔球 分别击倒的瓶子个数。
2.4 output format
一行一个整数,表示最大可能得分。
2.5 sample input
2
3 7
10 0
5 2
2.6 sample output
44
2.7 sample explanation
将第一轮和第三轮互换一下,可以得到最大分数。
2.8 constraints 对于30%的数据,n ≤ 9 对于100%的数据,n ≤ 50,0 ≤ ai + bi ≤ 10

分析

直接粘题解了,贪心的思路不错,但是题目放在OI比赛里真的不好

好多人随机化然后AC了,逃)

我第一次听到出题人被骂sb的(删去)(不是我)

首先把所有 全给 接成一串,这样除了最后一个 全给 所有的 全给 都提供了10的收益
枚举一下最后一个 全给 后面接的是什么,可能有(1) 正常 (2) 奥义 (3) 奥义 + 正常 3种情况
把剩下的 正常 放在所有 全给 前面,再把 奥义 向序列中插入
因为不会改变 全给 获得的收益,只会得到一个第一投分数的收益,直接贪心就行
注意如果给了n+1行的话就必须保证序列倒数第二个是 全给 ,所以不存在(3)的情况

code

随机化

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<ctime>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define dep(i,a,b) for(int i=a;i>=b;i--)
#define ll long long
#define mem(x,num) memset(x,num,sizeof x)
#define reg(x) for(int i=last[x];i;i=e[i].next)
using namespace std;
inline ll read()
{
    ll f=1,x=0;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const int maxn=56;
int n;
bool flag=false;
pair<int,int>a[maxn];

int calc(){
    if((flag)&&(a[n-1].first!=10))return -1;
    int ans=0,p=1,q=1;
    rep(i,1,n){
        ans+=a[i].first*p+a[i].second*q;
        if(a[i].first==10)p=q=2;
        else if(a[i].first+a[i].second==10)p=2,q=1;
        else p=q=1;
    }
    return ans;
}
int main()
{
    srand(time(0));
    n=read();
    rep(i,1,n)a[i].first=read(),a[i].second=read();
    if(a[n].first==10){
        n++;flag=true;
        a[n].first=read(),a[n].second=read();
    }
    int T=1000000,ans=calc();
    while(T--){
        int p=rand()%n+1,q=rand()%n+1;
        swap(a[p],a[q]);
        int ths=calc();
        if(ths>=ans)ans=ths;else swap(a[p],a[q]);
    }
    cout<<ans<<endl;
    return 0;
}

标程

#include <bits/stdc++.h>
using namespace std;
int N, n, a, b;
int strick;
vector<int>spare, normal;
int maxsum[20];
int base;
int ans, now;
int main() {
    freopen("dierti10.in","r",stdin);
    freopen("dierti10.out","w",stdout);
    scanf("%d", &N);
    while (scanf("%d%d", &a, &b) != EOF) {
        n++;
        base += a + b;
        if (a + b != 10) {
            normal.push_back(a);
            maxsum[a] = max(maxsum[a], a + b);
        } else if (a != 10) {
            spare.push_back(a);
        } else {
            strick++;
        }
    }
    if (strick) {
        base += (strick - 1) * 10;
    }
    sort(spare.begin(), spare.end());
    reverse(spare.begin(), spare.end());
    for (int i = 0; i < int(normal.size()); i++) {
        now = strick ? maxsum[normal[i]] : 0;
        priority_queue<int>q;
        for (int j = 1; j <= strick; j++) {
            q.push(10);
        }
        for (int j = 0; j < int(normal.size()); j++) {
            if (!strick || j != i) {
                q.push(normal[j]);
            }
        }
        for (int j = 0; j < int(spare.size()); j++) {
            if (!q.empty()) {
                now += q.top();
                q.pop();
            }
            q.push(spare[j]);
        }
        ans = max(ans, now);
    }
    for (int i = 0; i < int(spare.size()); i++) {
        now = strick ? 10 : 0;
        priority_queue<int>q;
        for (int j = 1; j <= strick; j++) {
            q.push(10);
        }
        for (int j = 0; j < int(normal.size()); j++) {
            q.push(normal[j]);
        }
        q.push(spare[i]);
        for (int j = 0; j < int(spare.size()); j++) {
            if (j != i) {
                if (!q.empty()) {
                    now += q.top();
                    q.pop();
                }
                q.push(spare[j]);
            }
        }
        ans = max(ans, now);
    }
    if (n == N) {
        for (int i = 0; i < int(normal.size()); i++) {
            for (int k = 0; k < int(spare.size()); k++) {
                now = normal[i];
                now += strick ? 10 : 0;
                priority_queue<int>q;
                for (int j = 1; j <= strick; j++) {
                    q.push(10);
                }
                for (int j = 0; j < int(normal.size()); j++) {
                    if (j != i) {
                        q.push(normal[j]);
                    }
                }
                q.push(spare[k]);
                for (int j = 0; j < int(spare.size()); j++) {
                    if (j != k) {
                        if (!q.empty()) {
                            now += q.top();
                            q.pop();
                        }
                        q.push(spare[j]);
                    }
                }
                ans = max(ans, now);
                if (now == 37) {
                    printf("%d %d\n", normal[i], spare[k]);
                }
            }
        }
    }
    printf("%d\n", ans + base);
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值