Color Length UVA1625(DP)

本文详细解析UVa1625颜色长度问题,介绍状态转移方程及预处理方法,对比不同实现方式并分享AC代码。讨论了如何避免使用memset初始化数组以减少运行时间。

这题状态转移方程倒是很好想,就是那个预处理有点麻烦,没办法最后还是百度......百度后还是T......

借鉴的几个博客:点击打开链接   点击打开链接


预处理方面,knm[i][j]表示n,m两个序列n取走i个m取走j个后已经开始且没有结束的颜色的花费.

刘汝佳标程里处理是先在i为0时扫一遍j,然后每个knm[i][j]的j部分就由上一层计算得来.


第二个卡我的点是memset,我因为它超时了,我觉得不应该啊,,,


先看刘汝佳滚动数组的写法,用 ^ 来滚动.而且是小数组memset不会超时.

// UVa1625 Color Length
// Rujia Liu
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int maxn = 5000 + 5;
const int INF = 1000000000;

char p[maxn], q[maxn]; // starts from position 1
int sp[26], sq[26], ep[26], eq[26]; // sp[i] start positions of character i in p
int d[2][maxn], c[2][maxn]; // 滚动数组 c[i][j]: how many "incomplete" colors in the mixed sequence

int main()
{
    int T;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%s%s", p+1, q+1);

        int n = strlen(p+1);
        int m = strlen(q+1);
        for(int i = 1; i <= n; i++) p[i] -= 'A';
        for(int i = 1; i <= m; i++) q[i] -= 'A';

        // calculate s and e
        for(int i = 0; i < 26; i++)
        {
            sp[i] = sq[i] = INF;
            ep[i] = eq[i] = 0;
        }
        for(int i = 1; i <= n; i++)
        {
            sp[p[i]] = min(sp[p[i]], i);
            ep[p[i]] = i;
        }
        for(int i = 1; i <= m; i++)
        {
            sq[q[i]] = min(sq[q[i]], i);
            eq[q[i]] = i;
        }

        // dp
        int t = 0;
        memset(c, 0, sizeof(c));
        memset(d, 0, sizeof(d));
        for(int i = 0; i <= n; i++)
        {
            for(int j = 0; j <= m; j++)
            {
                if(!i && !j) continue;

                // calculate d
                int v1 = INF, v2 = INF;
                //计算d[i][j],  d[i][j]由d[i-1][j]或d[i][j-1]添加一个字母得到
                if(i) v1 = d[t^1][j] + c[t^1][j]; // remove from p
                if(j) v2 = d[t][j - 1] + c[t][j - 1]; // remove from q
                d[t][j] = min(v1, v2);

                // calculate c
                if(i)
                {
                    c[t][j] = c[t^1][j];
                    if(sp[p[i]] == i && sq[p[i]] > j) c[t][j]++;            //出现新的字母
                    if(ep[p[i]] == i && eq[p[i]] <= j) c[t][j]--;           //一个字母已经结束
                }
                else if(j)
                {
                    c[t][j] = c[t][j - 1];
                    if(sq[q[j]] == j && sp[q[j]] > i) c[t][j]++;
                    if(eq[q[j]] == j && ep[q[j]] <= i) c[t][j]--;
                }
                printf("%d %d %d\n", c[t][j], i, j);
            }
            t ^= 1;
        }
        printf("%d\n", d[t^1][m]);
    }
    return 0;
}

我T了的代码们:
/*
#include <bits/stdc++.h>
using namespace std;
#define INF 1000000
typedef pair<int, int> P;
#define f first
#define s second
int dp[5002][5002];
int knm[5002][5002];
int ln, lm;
char n[5005], m[5005];
P saen[30], saem[30];
int main()
{
    int T;

    scanf("%d", &T);

    while(T--)
    {
        scanf("%s%s", n + 1, m + 1);
        ln = strlen(n + 1), lm = strlen(m + 1);

        for(int i = 0; i <= 26; i++){
             saen[i].f = saem[i].f = INF;
             saen[i].s = saem[i].s = 0;
        }

        for(int i = 1; i <= ln; i++){
            n[i] -= 'A';
            saen[n[i]].f = min(saen[n[i]].f, i);
            saen[n[i]].s = i;
        }
        for(int i = 1; i <= lm; i++){
            m[i] -= 'A';
            saem[m[i]].f = min(saem[m[i]].f, i);
            saem[m[i]].s = i;
        }

        memset(knm, 0, sizeof knm);
        memset(dp, 0, sizeof dp);

        for(int i = 0; i <= ln; i++){
            for(int j = 0; j <= lm; j++){
                if(!i && !j)
                    continue;
                int v1 = INF, v2 = INF;
                if(i) v1 = dp[i - 1][j] + knm[i - 1][j];
                if(j) v2 = dp[i][j - 1] + knm[i][j - 1];
                dp[i][j] = min(v1, v2);
                if(i){
                knm[i][j] = knm[i - 1][j];
                if(saen[n[i]].f == i && saem[n[i]].f > j) knm[i][j]++;            
                if(saen[n[i]].s == i && saem[n[i]].s <= j) knm[i][j]--;          
            }
            else if(j){
                knm[i][j] = knm[i][j - 1];
                if(saem[m[j]].f == j && saen[m[j]].f > i) knm[i][j]++;
                if(saem[m[j]].s == j && saen[m[j]].s <= i) knm[i][j]--;
            }
        }
    }

        printf("%d\n", dp[ln][lm]);
    }

    return 0;
}
*/

/*
#include <bits/stdc++.h>
using namespace std;
#define INF 1000000
typedef pair<int, int> P;
#define f first
#define s second
int dp[5002][5002];
int knm[5002][5002];
int ln, lm;
char n[5005], m[5005];
P saen[30], saem[30];
int main()
{
    int T;

    scanf("%d", &T);

    while(T--)
    {
        scanf("%s%s", n + 1, m + 1);
        ln = strlen(n + 1), lm = strlen(m + 1);

        for(int i = 0; i <= 26; i++){
             saen[i].f = saem[i].f = INF;
             saen[i].s = saem[i].s = 0;
        }

        for(int i = 1; i <= ln; i++){
            int j = n[i] - 'A';
            saen[j].f = min(saen[j].f, i);
            saen[j].s = i;
        }
        for(int i = 1; i <= lm; i++){
            int j = m[i] - 'A';
            saem[j].f = min(saem[j].f, i);
            saem[j].s = i;
        }

        memset(knm, 0, sizeof knm);
        memset(dp, 0, sizeof dp);
        for(int i = 0; i <= ln; i++)
            for(int j = 0; j <= lm; j++)
        {
            if(i){
                knm[i][j] = knm[i - 1][j];
                if(saen[n[i] - 'A'].f == i && saem[n[i] - 'A'].f > j) knm[i][j]++;
                if(saen[n[i] - 'A'].s == i && saem[n[i] - 'A'].s <= j) knm[i][j]--;
            }
            else if(j){
                knm[i][j] = knm[i][j - 1];
                if(saem[m[j] - 'A'].f == j && saen[m[j] - 'A'].f > i) knm[i][j]++;
                if(saem[m[j] - 'A'].s == j && saen[m[j] - 'A'].s <= i) knm[i][j]--;
            }
        }

        for(int i = 0; i <= ln; i++){
            for(int j = 0; j <= lm; j++){
                if(!i && !j)
                    continue;
                int v1 = INF, v2 = INF;
                if(i) v1 = dp[i - 1][j] + knm[i - 1][j];
                if(j) v2 = dp[i][j - 1] + knm[i][j - 1];
                dp[i][j] = min(v1, v2);
            }
        }

        printf("%d\n", dp[ln][lm]);
    }

    return 0;
}
都是因为memsetT的,看了别人的代码,人家根本没有刷新数组,其实想一想真的不用,dp的边界dp[0][0]永远是0,knm的边界knm[0][0]也是.

AC代码:
#include <bits/stdc++.h>
using namespace std;
#define INF 1000000

int dp[5002][5002];
int knm[5002][5002];
int ln, lm;
char n[5005], m[5005];
int sn[26], sm[26], en[26], em[26];
int main()
{
    int T;

    scanf("%d", &T);

    while(T--)
    {
        scanf("%s%s", n + 1, m + 1);
        ln = strlen(n + 1), lm = strlen(m + 1);

        for(int i = 0; i <= 26; i++)
        {
            sn[i] = sm[i] = INF;
            en[i] = em[i] = 0;
        }

        for(int i = 1; i <= ln; i++)
        {
            n[i] -= 'A';
            sn[n[i]] = min(sn[n[i]], i);
            en[n[i]] = i;
        }
        for(int i = 1; i <= lm; i++)
        {
            m[i] -= 'A';
            sm[m[i]] = min(sm[m[i]], i);
            em[m[i]] = i;
        }

       // memset(knm, 0, sizeof knm);
        //memset(dp, 0, sizeof dp);

        for(int i = 0; i <= ln; i++)
        {
            for(int j = 0; j <= lm; j++)
            {
                if(!i && !j)
                    continue;
                int v1 = INF, v2 = INF;
                if(i) v1 = dp[i - 1][j] + knm[i - 1][j];
                if(j) v2 = dp[i][j - 1] + knm[i][j - 1];
                dp[i][j] = min(v1, v2);

                if(i)
                {
                    knm[i][j] = knm[i - 1][j];
                    if(sn[n[i]] == i && sm[n[i]] > j) knm[i][j]++; 
                    if(en[n[i]] == i && em[n[i]] <= j) knm[i][j]--;          
                }
                else if(j)
                {
                    knm[i][j] = knm[i][j - 1];
                    if(sm[m[j]] == j && sn[m[j]] > i) knm[i][j]++;
                    if(em[m[j]] == j && en[m[j]] <= i) knm[i][j]--;
                }
            }
        }

        printf("%d\n", dp[ln][lm]);
    }

    return 0;
}


再看看刘汝佳的滚动数组,婉转回肠地唉一声,,,,,,



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值