DP——Hard problem (Codeforces Round #367 (Div. 2) C)

  • 题目链接:
    http://codeforces.com/contest/706/problem/C

  • 分析:
    给出n个字符串和把每个字符串翻转所需要的费用 Ci ,每个字符串都可以选择翻转或者不翻转,求将n个字符串变成字典序所需要的最小费用,若不能变成字典序则输出-1.

    字典序: 若在字典序上A在B前面则A是B的前缀 或者 A和B第一个不相同的字符是A[i]<B[i]。

  • 题解:
    设dp[i][1]/[0] 表示第i个字符串翻转/不翻转时构成字典序的最小cost。

初始化: dp[1][0] = 0, dp[1][1] = cost[1];
(str[i]表示第i个字符串, ~str[i]表示第i个字符串翻转)
第一个判断:
str[i] > str[i-1] && str[i]> ~str[i-1] dp[i][0] = min(dp[i][0], min(dp[i-1][0], dp[i-1][1]) );
str[i] > str[i-1] && str[i]< ~str[i-1] dp[i][0] = min(dp[i][0], dp[i-1][0]);
str[i] < str[i-1] && str[i]> ~str[i-1] dp[i][0] = min(dp[i][0], dp[i-1][1]);
str[i] < str[i-1] && str[i]< ~str[i-1] dp]i][0] = INF;
第二个判断:
~str[i] > str[i-1] && str[i]> ~str[i-1] dp[i][1] = min(dp[i][1], min(dp[i-1][0], dp[i-1][1])+cost[i] );
~str[i] > str[i-1] && str[i]< ~str[i-1] dp[i][1] = min(dp[i][1], dp[i-1][0]+cost[i] );
~str[i] < str[i-1] && str[i]> ~str[i-1] dp[i][1] = min(dp[i][1], dp[i-1][1]+cost[i] );
~str[i] < str[i-1] && str[i]< ~str[i-1] dp]i][1] = INF;

  • AC代码:
    这里写图片描述
/*************************************************************************
    > File Name: test.cpp
    > Author: Akira 
    > Mail: qaq.febr2.qaq@gmail.com 
 ************************************************************************/

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
#include <cmath>
#include <vector>
#include <set>
#include <list>
#include <ctime>
typedef long long LL;
typedef unsigned long long ULL;
typedef long double LD;
#define MST(a,b) memset(a,b,sizeof(a))
#define CLR(a) MST(a,0)
#define Sqr(a) ((a)*(a))
using namespace std;

#define MaxN 100005
#define MaxM MaxN*10
#define INF (LL)1e15
#define bug cout<<88888888<<endl;
int n;
LL cost[MaxN];

string str[MaxN];
string Rstr[MaxN];

LL dp[MaxN][2];  

string Re(string str)
{
    string s;
    for(int i=str.length()-1;i>=0;i--)
    {
        s+=str[i];
    }
    return s;
}

bool judge(string a, string b)
{
    if(a==b) return true;

    int alen = a.length();
    int blen = b.length();
    if(alen > blen)
    {
        int i=0;
        while(a[i]==b[i] && i<blen) i++;
        if(i>=blen) return true;
        if( a[i]>b[i] ) return true;
        else  return false;
    }
    else 
    {
        int i=0;
        while(a[i]==b[i] && i<alen) i++;
        if(i>=alen) return false;
        if( a[i]>b[i] ) return true;
        else  return false;
    }
}

void solve()
{
    for(int i=1;i<=n;i++)
    {
        Rstr[i] = Re(str[i]);
        //cout << Rstr[i] << endl;
    }
    for(int i=1;i<=n;i++)
    {
        dp[i][0]=INF;
        dp[i][1]=INF;
        //cout << dp[i][1] << endl;
    }
    dp[1][0] = 0;
    dp[1][1] = cost[1];

    for(int i=2;i<=n;i++)
    {
        int flag0 = judge(str[i], str[i-1]);
        int flag1 = judge(str[i], Rstr[i-1]);
        //cout << flag0 << " " << flag1 << endl;
        if( flag0 && flag1 ) dp[i][0] = min(dp[i][0], min(dp[i-1][0], dp[i-1][1]) );
        if( flag0 && !flag1 ) dp[i][0] = min(dp[i][0], dp[i-1][0]);
        if( !flag0 && flag1 ) dp[i][0] = min(dp[i][0], dp[i-1][1]);
        if( !flag0 && !flag1 ) dp[i][0] = INF;

        flag0 = judge(Rstr[i], str[i-1]);
        flag1 = judge(Rstr[i], Rstr[i-1]);
        if( flag0 && flag1 ) dp[i][1] = min(dp[i][1], min(dp[i-1][0], dp[i-1][1])+cost[i] );
        if( flag0 && !flag1 ) dp[i][1] = min(dp[i][1], dp[i-1][0]+cost[i] );
        if( !flag0 && flag1 ) dp[i][1] = min(dp[i][1], dp[i-1][1]+cost[i] );
        if( !flag0 && !flag1 ) dp[i][1] = INF;
        //cout << flag0 << " " << flag1 << endl;

        //cout << dp[i][0] << " " << dp[i][1] << endl;
    }
}


int main()
{
    while(~scanf("%d", &n))
    {
        for(int i=1;i<=n;i++)
            scanf("%I64d", &cost[i]);

        for(int i=1;i<=n;i++)
        {
            cin >> str[i];
        }

        solve();
        LL ans = min(dp[n][0], dp[n][1]);

        if(ans >= INF) cout << -1 << endl;
        else cout << ans << endl;
    }

    //system("pause");
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值