CodeForce 706C 动态规划 利用下标表示状态 递推 (伪状态压缩)

本文介绍了一种O(n)的算法解决方案,用于处理一系列字符串的比对与操作问题。通过使用滚动数组和巧妙的变量切换技巧,有效地解决了大规模数据集上的字符串序列处理难题。

         好吧,在比赛的时候最后20分钟才看这道题目,当时的想法是既然数据量有100000,而且又只给1000ms,那么应该是O(n)的算法了,当时就想到了利用递推。。。可是没有去写,也许是没有仔细的考虑清楚吧,最终也是比赛之后才写出来。。。

        在后来解决这道题目的时候我还是用了一点小技巧。。不然会爆内存的,用了一个滚动数组,因为我们可以很明显的看出来,当一个新的字符串加入的时候,我们的答案变化仅仅是由新加入的字符串和之前的尾部的字符串共同决定的。另外一个我用的很多的小技巧就是让一个变量在0和1之间不断的来回变动,x = 1 - x 这条语句可是使得x 在0到1之间不断的往返,来作为我的数组下标。

        总体思路就是不断的读取一个新的字符串,来和之前的尾部字符串相比较,从而决定答案,check函数接受4个参数,第一个是第 i 个字符串 ,第二个是第 i - 1个字符串,第三个和第四个参数表示 i 和 i -1是否反转,初始化的时候数组里面有一个空串,假若一条路走不通了,就赋值为无穷大,同时跳过和答案为无穷大的字符串的比较,com是自己手写的比较函数,以下是代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<climits>

using namespace std;

const long long inf=LLONG_MAX-0x3f3f3f3f;
char arr[2][50005];
long long ans[2][2];
int cost[100005],n;
bool check(int i),com(char* si, char* si_1,bool si_reverse,bool si_1_reverse),flag[4];;

int main(){
    arr[0][0]=arr[0][1]=0;
    scanf("%d",&n);
    for(int i=1;i<=n;++i)
        scanf("%d",&cost[i]);
    for(int i=1;i<=n;++i){
        scanf("%s",arr[i&1]);
        if(!check(i)){
            printf("%d\n",-1);
            return 0;
        }
    }
    printf("%lld\n",min(ans[n&1][0],ans[n&1][1]));
    return 0;
}

bool check(int i){
    memset(flag,0,sizeof(flag));
    ans[i&1][0]=ans[i&1][1]=inf;
    if(ans[1-(i&1)][0]!=inf&&com(arr[i&1],arr[1-(i&1)],false,false)){
        flag[0]=true,ans[i&1][0]=ans[1-(i&1)][0];
    }
    if(ans[1-(i&1)][1]!=inf&&com(arr[i&1],arr[1-(i&1)],false,true)){
        flag[1]=true,ans[i&1][0]=min(ans[i&1][0],ans[1-(i&1)][1]);
    }
    if(ans[1-(i&1)][0]!=inf&&com(arr[i&1],arr[1-(i&1)],true,false)){
        flag[2]=true,ans[i&1][1]=ans[1-(i&1)][0]+cost[i];
    }
    if(ans[1-(i&1)][1]!=inf&&com(arr[i&1],arr[1-(i&1)],true,true)){
        flag[3]=true,ans[i&1][1]=min(ans[i&1][1],ans[1-(i&1)][1]+cost[i]);
    }
    if(!(flag[0]||flag[1]||flag[2]||flag[3]))
        return false;
    return true;
}

bool com(char* si, char* si_1,bool si_reverse,bool si_1_reverse){
    int a =strlen(si),b=strlen(si_1),i,k;
    if(b==0)
        return true;
    if(!si_reverse&&!si_1_reverse){
        return strcmp(si,si_1)>=0;
    }
    else if(!si_reverse&&si_1_reverse){
        for(i=0,k=b-1;i<a&&k>=0;++i,--k)
        if(si[i]<si_1[k])
            return false;
        else if(si[i]>si_1[k])
            return true;
        if(i>=a&&k>=0)
           return false;
        return true;
    }
    else if(si_reverse&&!si_1_reverse){
        for(i=a-1,k=0;i>=0&&k<b;--i,++k)
        if(si[i]<si_1[k])
            return false;
        else if(si[i]>si_1[k])
            return true;
        if(i<0&&k<b)
            return false;
        return true;
    }
    else{
        for(i=a-1,k=b-1;i>=0&&k>=0;--i,--k)
        if(si[i]<si_1[k])
            return false;
        else if(si[i]>si_1[k])
            return true;
        if(i<k)
            return false;
        return true;
    }
}

(其实写的这么长我自己都不敢看。。。)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值