[区间DP] 洛谷P1026 统计单词个数(预处理)

题目

LP1026

思路

本题很明显的是区间DP,但是多了个划分次数的量k,当k还小于kk(题目给的k)的时候就还得继续划分。没关系,本题常数小,状态上还可以加个k。
1.状态定义:d(i,j,k),区间[i,j]内已经划分了次数k时,最大的单词数。
2.状态转移:
(1).当k = kk时,直接计算区间[i,j]的单词数即可。

d[i][j][k]=c[i][j] d [ i ] [ j ] [ k ] = c [ i ] [ j ]

(2).当k < kk时,枚举l∈[i,j),就得到两个区间[i,l],[l+1,j],这两个区间选一个作为成品区间让它k=kk,另外一个继续让它再划分即可。
d[i][j][k]=max{dp(i,l,kk)+dp(l+1,j,k+1),dp(i,l,k+1)+dp(l+1,j,kk)} d [ i ] [ j ] [ k ] = m a x { d p ( i , l , k k ) + d p ( l + 1 , j , k + 1 ) , d p ( i , l , k + 1 ) + d p ( l + 1 , j , k k ) }



还有个问题是本题的预处理,也就是先算出cc[i][j],区间[i,j]的单词数。
我用了个递归解决,具体看代码即可。

代码

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string>
#include <set>
#include <iostream>
#define _for(i,a,b) for(int i = (a); i<(b); i++)
#define _rep(i,a,b) for(int i = (a); i<=(b); i++)
using namespace std;

const int INF = 10000;
const int maxn = 200+10;
const int maxz = 6 + 4;
const int maxk = 40 + 4;
int n, kk, p, dicnum, cc[maxn][maxn], d[maxn][maxn][maxk];  
// cc[i][j]:[i,j]区间含有单词数,d[i][j][k]:[i,j]区间,已经划分了k次,的最大单词数
string s;
set<string> dic;  // 常数很小,用set偷懒

int getcc(int i, int j) {
    if (i == j) {
        if (dic.count(s.substr(i, 0))) { cc[i][j] = 1; return 1; }
        else { cc[i][j] = 0; return 0; }
    }
    bool have = false;
    for (set<string>::iterator iter = dic.begin(); iter != dic.end(); iter++) {
        if (s.substr(i).find(*iter) == 0 && (*iter).length() <= j-i+1) have = true;
    }
    int c = getcc(i + 1, j);
    if (have) { cc[i][j] = c+1; return c+1; }
    else { cc[i][j] = c; return c; }
}

int dp(int i, int j, int k) {
    if (i > j) return -INF;

    int &ans = d[i][j][k];
    if (ans != -1) return ans;

    ans = -INF;
    if (k == kk)
        ans = max(ans, cc[i][j]);
    else {
        _rep(l, i, j - 1) {
            ans = max(ans, dp(i, l, kk) + dp(l + 1, j, k+1));
            ans = max(ans, dp(i, l, k + 1) + dp(l + 1, j, kk));
        }
    }
    return ans;
}

int main() {
    // 输入
    cin >> p >>kk;
    string temp;
    _for(i, 0, p) {
        cin >> temp;
        s += temp;
    }
    n = s.length();
    cin >> dicnum;
    _for(i, 0, dicnum) {
        cin >> temp;
        dic.insert(temp);
    }

    for (int j = n - 1; j > 0; j--) 
        getcc(0, j);

    memset(d, -1, sizeof(d));
    printf("%d\n", dp(0, n - 1, 1));

    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值