Codeforces748D-Santa Claus and a Palindrome(贪心,构造)

14 篇文章 0 订阅
4 篇文章 0 订阅

题目链接

http://codeforces.com/contest/748/problem/D

思路

只要值大于0,那么一定对我们的结果有贡献,那么尽可能的去贪心
首先,不是回文串的字符串我们必须找到它对称的字符串,一起放两边。是回文串的字符串,可能用两个放两边,也可能选出一个放中间。

我们用rem来保存放中间的那个回文串的值,oth保存放两边的回文串(如果其中有一个回文串的值小于0)的小于0的那个值
用大顶堆去维护每个字符串最大的那个值

对于每个字符串判断其是否是回文

S1:如果不是回文,它的对称字符串是否存在

    S11:如果存在且它俩的和大于0,那么就把它们加入到结果中。

    S12:如果不满足上述条件,终止

S2:如果是回文,统计其个数,如果为1,跳转到S21,否则到S22

    S21:如果其个数为1且值大于0,那么我们可能把它加入到最中间,用rem来保存最中间的值,如果其值大于rem,更新rem。

    S22:如果其个数多于1个,那么我们可能把它放到两边或者中间。
          如果连续两个的和大于0,那么我们先把它俩放到两边,sum累加。如果第二个的值小于0的话,我们更新oth为min(oth, 第二个)(这代表可能将这个回文串放到最中间)。
          如果连续两个的和小于0,如果第一个的和大于0,我们更新rem为max(rem, 第一个)(这代表第一个可能放中间)

最后,我们考虑把哪个回文串放中间(rem or oth)

(1)之前保存的单独的一个rem

(2)从放两边的回文串(有一个值为负)里面取负的最多的去掉,剩下的那个正值放中间。

于是,我们只需要比较rem和|oth|的值即可

代码

#include <bits/stdc++.h>

using namespace std;

inline int in() {int x; scanf("%d", &x); return x;}
#define pr(x) {cout << #x << ' ' << x << endl;}
#define LL long long

const int maxn = 100000 + 5;
int n, k;

bool is_par(string s) {
    int i = 0, j = s.length() - 1;
    while (1) {
        if (i >= j) break;
        if (s[i] != s[j]) return false;
        i++; j--;
    }
    return true;
}

int tcnt = 0;
map<string, int> m1;
map<int, string> m2;
priority_queue<int> q[maxn];
LL sum = 0;

int main() {
    k = in(); n = in();
    for (int i = 0; i < k; i++) {
        char s[maxn];
        scanf("%s", s);
        int x = in();
        if (m1.find(s) == m1.end()) {
            m1[s] = ++tcnt;
            m2[tcnt] = s;
            q[tcnt].push(x);
        } else {
            int ind = m1[s];
            q[ind].push(x);
        }
    }
    int rem = -INT_MAX;
    int oth = 0;
    for (int i = 1; i <= tcnt; i++) {
        string s = m2[i];
        if (is_par(s)) {
            while (!q[i].empty()) {
                if (q[i].size() == 1) {
                    if (q[i].top() > 0) {
                        rem = max(rem, q[i].top());
                        break;
                    } else {
                        break;
                    }
                } else {
                    int x = q[i].top(); q[i].pop();
                    if (x + q[i].top() > 0) {
                        sum += (LL)(x + q[i].top());
                        if (q[i].top() < 0) oth = min(q[i].top(), oth);
                        q[i].pop();
                    } else {
                        if (x > 0) {
                            rem = max(rem, x);
                            break;
                        } else {
                            break;
                        }
                    }
                }
            }
        } else {
            string s2 = s;
            reverse(s2.begin(), s2.end());
            if (m1.find(s2) == m1.end()) continue;
            int j = m1[s2];
            while (!q[i].empty() && !q[j].empty() && q[i].top() + q[j].top() > 0) {
                sum += (LL)(q[i].top() + q[j].top());
                q[i].pop(); q[j].pop();
            }
        }
    }
    if (rem > abs(oth)) sum += (LL)rem;
    else sum -= (LL)oth;
    cout << sum << endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值