codeforces 748D Santa Claus and a Palindrome (构造+贪心+数据结构)

D. Santa Claus and a Palindrome

Santa Claus likes palindromes very much. There was his birthday recently. k of his friends came to him to congratulate him, and each of them presented to him a string si having the same length n. We denote the beauty of the i-th string by ai. It can happen that ai is negative — that means that Santa doesn't find this string beautiful at all.

Santa Claus is crazy about palindromes. He is thinking about the following question: what is the maximum possible total beauty of a palindrome which can be obtained by concatenating some (possibly all) of the strings he has? Each present can be used at most once. Note that all strings have the same length n.

Recall that a palindrome is a string that doesn't change after one reverses it.

Since the empty string is a palindrome too, the answer can't be negative. Even if all ai's are negative, Santa can obtain the empty string.

Input

The first line contains two positive integers k and n divided by space and denoting the number of Santa friends and the length of every string they've presented, respectively (1 ≤ k, n ≤ 100 000; n·k  ≤ 100 000).

k lines follow. The i-th of them contains the string si and its beauty ai ( - 10 000 ≤ ai ≤ 10 000). The string consists of n lowercase English letters, and its beauty is integer. Some of strings may coincide. Also, equal strings can have different beauties.

Output

In the only line print the required maximum possible beauty.


题目大意:给定 n 个字符串,每个字符串有一个价值 val,让你通过这些给定的字符串拼出一个总价值最大的回文串(palindrome)。所有字符串长度相同。
解题思路:总体上是一种贪心的想法。
                1、先将所有的字符串尽量两两配对形成回文串,保证每两个字符组成的回文串的总价值大于零。
                2、考虑比较特殊的情况,最后形成的回文串中间可以放一个自对称的字符串,aaa xyx aaa
                3、考虑如何选出那个放在中间的自对称字符串,有两种情况:
                     一种是不能配对的自对称字符串(只有一个或只有奇数个)
                    另一种是从已经配对的字符串中选取一个值最大的字符串出来(与它配对的字符串值为负)
              
               最后采用合适的数据结构储存字符串到对应值序列的映射关系
                     我采用的是map< string , multiset<int> >

注意事项:multiset删除元素的操作不能直接实用erase( val );这样会同时删除所有值等于val的元素,如果想要只删除其中一个元素,需要采用erase(set.find(val))的办法
AC代码:
#include <bits/stdc++.h>

#define Fori(x) for(int i=0;i<x;i++)
#define Forj(x) for(int j=0;j<x;j++)
#define maxn 5005
#define inf 0x3f3f3f3f
#define ONES(x) __builtin_popcount(x)
using namespace std;

typedef long long ll ;
const double eps =1e-8;
const int mod = 1000000007;
typedef pair<int, int> P;
const double PI = acos(-1.0);
int dx[4] = {0,0,1,-1};
int dy[4] = {1,-1,0,0};

int n,k,m;
ll ans;

struct Node{
    string s;
    int val;
}node[100005];
map<string,multiset<int> > dic;//multiset储存多个相同大小的值
bool cmp(Node a, Node b)
{
    return a.val > b.val;
}

int main()
{
    freopen("test.txt","r",stdin);
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    cin>>n>>m;
    for(int i = 0; i<n; i++)
    {
        cin>>node[i].s;
        cin>>node[i].val;
        dic[node[i].s].insert(node[i].val);
    }
    int k = 0;int x = 0;int temp = 0;
    for(int i =0; i<n; i++)
    {
        string s1 = node[i].s;
        int val = (*dic[s1].rbegin());//the last val of the set
        if(dic[s1].size()==0) 
            continue;        
        if(val<0)  
            continue;
        reverse(s1.begin(),s1.end());//s1->对称字符串
        
        dic[node[i].s].erase(dic[node[i].s].find(val));
        
        if(dic[s1].size()!=0)
        {
            temp = (*dic[s1].rbegin());//选出配对字符串的最大值
            dic[s1].erase(dic[s1].find(temp));
            if(val+temp>=0){
                ans+=(val+temp);
                if(s1==node[i].s)
                {
                    x = max(-temp,x);//将差值加入到中间字符串的选择过程中
                }
            }
            else if(s1==node[i].s)
                x = max(x,val);//选择中间的自对称字符串
        }
        else if(s1==node[i].s)
            x = max(x,val);//选择中间的自对称字符串
    }
    cout << ans+x << endl;//结果补上中间的字符串的值
    return 0;
}

最后晒一下我为写这道题付出的艰苦努力。。。都是泪/(ㄒoㄒ)/~~

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值