Magic Potions


题目描述:

一堆东西,每次拿出两个不同的东西合。要最终合出来的最多。并且要贪心的买12 13 14.。。1n 23.。。
http://codeforces.com/gym/100430/attachments/download/2418/20092010-summer-petrozavodsk-camp-andrew-stankevich-contest-36-asc-36-en.pdf

题解:

首先要保证总个数是最多的。怎么算总个数?求一下maxelement,然后sum-maxelement和max值比较,大中小对应情况。
其次要贪心的去取,那么我们最直接的:取12 取13.。。。可以取就一直取,如果取完算一下剩下的东西不能凑成原来的最优的结果的话,那么二分找到最大能够取得那个值。把它给取了。之后一旦发生这样的情况,说明已经到了后面的东西全部都和后面的最高值合并的特殊情况(这一点很重要),我们直接killit就好了。

另外也有一种其他方法:先贪心的合并,然后可能剩余一个有很多数量,再倒着拆已经合好的石头和这个剩余的合并。

重点:

(1)会算最大个数。
(2)按照题目中贪心。不能取的时候发现已经到了特殊情况。
(3)注意max=6 sum-max=7的情况。

代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <ctype.h>
#include <limits.h>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#include <stack>
#include <set>
#include <bitset>
#include <assert.h>
#define CLR(a) memset(a, 0, sizeof(a))
#define REP(i, a, b) for(ll i = a;i < b;i++)
typedef long long ll;

using namespace std;

const ll maxn = 1e5 + 100;
struct info
{
    ll a, b, num;
    info(ll _a = 0, ll _b = 0, ll _num = 0)
    {
        a = _a;
        b = _b;
        num = _num;
    }
    bool operator < (const info & other) const
    {
        if(a == other.a)
        {
            return b < other.b;
        }
        return a < other.a;
    }
};

ll maxA[maxn], sum, maxa, maxi;
ll a[maxn];
ll n;
ll tot;
vector<info> ans;
vector<info> ansPro;
void killIt(ll key)
{
    for(ll i =1; i<=n; i++)
    {
        if(i != key && a[i] > 0 && a[key]>0)
        {
            ll sub = min(a[i], a[key]);
            if(sub > 0)
            {
                ans.push_back(info(min(i, key), max(i, key), sub));
                a[i]-=sub;
                a[key] -= sub;
            }
        }
    }
}

void outPut()
{
    sort(ans.begin(), ans.end());
//    ll resSum = 0;
//    ansPro.clear();
//    for(ll i = 0; i<ans.size(); i++)
//    {
//        if(ans[i].num != 0)
//        {
//            ansPro.push_back(ans[i]);
//        }
//    }
//    ans.clear();
//    sort(ansPro.begin(), ansPro.end());
//    for(ll i = 0; i<ansPro.size(); i++)
//    {
//        resSum += ansPro[i].num;
//        if(i!=0)
//        {
//            if(ansPro[i].a == ans[ans.size()-1].a && ansPro[i].b == ans[ans.size()-1].b)
//            {
//                ans[ans.size()-1].num += ansPro[i].num;
//            }
//            else
//            {
//                ans.push_back(ansPro[i]);
//            }
//        }
//        else
//        {
//            ans.push_back(ansPro[i]);
//        }
//    }
    //ll tsum = 0;
    ll tempAns = ans.size();
    printf("%I64d\n", tempAns);
    for(ll i = 0; i<ans.size(); i++)
    {
        //tsum += ans[i].num;
        printf("%I64d %I64d %I64d\n", ans[i].a, ans[i].b, ans[i].num);
    }
    //printf("%d -----\n", tsum);
}
ll gettot(ll mm, ll sum)
{
    if(mm <= sum-mm)
        return sum/2;
    return sum-mm;
}
ll solve(ll &now1, ll &now2)
{
   ll sub = min(a[now1], a[now2]);
   ll tmpMax = max(a[now1],a[now2])-sub;
   ll tmpSum = sum - sub*2;
   tmpMax = max(maxA[now2+1], tmpMax);
   ll tmptot = gettot(tmpMax, tmpSum);

   if(tmptot+sub==tot)
   {
       sum = tmpSum;
       a[now1]-=sub;
       a[now2]-=sub;
       tot -= sub;
       ans.push_back(info(now1, now2, sub));
       if(a[now2]==0&&a[now1]==0)
       {
           now1=now2+1;
           now2=now1+1;
       }
       else if(a[now2]==0)
       {
           now2++;
       }
       else
       {
           now1 = now2;
           now2++;
       }
       return 1;
   }
   else
   {
       ll l = 0, r = sub;
       while(l < r)
       {
           ll mid = (l+r)/2;
           tmpSum = sum-2*mid;
           tmpMax = max(a[now1],a[now2])-mid;
           tmpMax = max(tmpMax, maxA[now2+1]);
           tmptot = gettot(tmpMax, tmpSum);
           if(tmptot+mid==tot)
           {
               l = mid+1;
           }
           else
           {
               r = mid;
           }
       }
       r--;
       if(r!=0)
        ans.push_back(info(now1, now2, r));
       ll maxI = now1, maxA = a[now1]-r;
       sum -= 2*r;
       a[now1] -= r;
       a[now2] -= r;
       for(ll i = now2;i<=n;i++)
       {
           if(a[i]>maxA)
           {
               maxA = a[i];
               maxI = i;
           }
       }
       killIt(maxI);
       return 0;
   }
}
void gao()
{
    sum = 0;
    for(ll i = 1; i<=n; i++)
    {
        sum += a[i];
    }
    maxi = max_element(a+1, a+1+n)-a;
    maxa = a[maxi];
    a[n+1] = 0;
    maxA[n+1]=0;
    for(ll i = n;i>=1;i--)
    {
        maxA[i]=max(maxA[i+1], a[i]);
    }
    ans.clear();
    if(!(maxa>=sum-maxa))
    {
        tot = sum/2;
        ll l = 1, r = 2;
        while(r<=n)
        {
            ll flag = solve(l, r);
            if(flag==0)
                break;
        }
    }
    else
    {
        killIt(maxi);
    }
    outPut();
}

int main()
{
    //freopen("11Kin.txt", "r", stdin);
    //freopen("1out.txt", "w", stdout);
    freopen("magic.in", "r", stdin);
    freopen("magic.out", "w", stdout);
    while(scanf("%I64d", &n)!=EOF)
    {
        for(ll i =1; i<=n; i++)
        {
            scanf("%I64d", &a[i]);
        }
        gao();
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值