uvalive3608(二分 + DP)

题目大意:
给出a,b两个串,a串可以分成若干串,问将a的每个分开的串分别转化为b串,最少的操作数是多少

思路:
二分答案,避免超时。
dp[i][j]表示在a串的第i个字符和j串的第j个字符最少的操作数是多少。
那么当a的i + 1个字符和b的j + 1个字符相等时候,它的最小操作数是和a到i个字符和b到j个字符的值是一样的。
那么当a到i个字符和b到j + 1个字符的时候,此时最小的操作数可能是a到i个字符和b到j个字符的值 + 1,不可能是相等的。
那么当a到i + 1个字符和b到j 个字符的时候,道理同上。

代码:

#include <iostream>
using namespace std;
#include <cstring>
#include <stdio.h>

const int INF = 0x3f3f3f3f;
int n,m;
char a[5555],b[555];
int dp[5555][555];

bool check(int mid) {
    for(int i = 0; i <= n; i++)
        for(int  j = 0; j <= m; j++)
            dp[i][j] = INF;
    dp[0][0] = 0;
    for(int i = 0; i <= n; i++) {
        if(dp[i][m] <= mid)
            dp[i][0] = 0;
        for(int j = 0; j <= m; j++) {
            if(dp[i][j] > mid)
                continue;
            dp[i + 1][j + 1] = min(dp[i + 1][j + 1],dp[i][j] + (a[i + 1] == b[j + 1]?0:1));
            dp[i][j + 1] = min(dp[i][j + 1],dp[i][j] + 1);
            dp[i + 1][j] = min(dp[i + 1][j],dp[i][j] + 1);
        }
    }
    if(dp[n][m] <= mid)
        return true;
    return false;
}

int main() {

    int T;
    scanf("%d",&T);
    while(T--) {
        scanf("%s%s",b+1,a+1);
        n = strlen(a + 1);
        m = strlen(b + 1);
        int l = 0,r = max(n,m);
        while(l < r) {
            int mid = (l + r)/2;
            if(check(mid)) {
                r =  mid;
            }
            else
                l = mid + 1;
        }
        printf("%d\n",l);
    }
    return 0;
}

你可能感兴趣的:(uvalive3608(二分 + DP))