题目:https://www.luogu.org/problemnew/show/P1026
题意:
给定一个字符串,要求把他分成k段。给定s个单词,问划分成k段之后每段中包含的单词和最大是多少。
一个位置作为单词的开头只能计算一次。
思路:
如果仅仅是统计某一个区间内的最大单词数,这比较简单。每次从后往前推一个字符,如果这个字符是一个单词的开头,那么$cnt[i][j] = cnt[i+1][j]+1$
但是现在要分成$j$段,这是状态之一,另一个状态应该是已经考虑前$i$个字符。
也就是用$dp[i][j]$表示前$i$个字符划分成$j$段的最大单词数。
很显然$dp[i][j] = dp[d][j-1] + cnt[d +1][i]$,也就是说在$0~i$之间找到一个位置$d$,从这里断开,前面的组成$j-1$段,后面的作为一个新的段。
需要考虑的是,一个只有$i$个字符的字符串,最多只能划分成$i$段。
并且要注意当$j =1$时需要特殊处理。
1 #include<cstdio> 2 #include<cstdlib> 3 #include<map> 4 #include<set> 5 #include<cstring> 6 #include<algorithm> 7 #include<vector> 8 #include<cmath> 9 #include<stack> 10 #include<queue> 11 #include<iostream> 12 13 #define inf 0x7fffffff 14 using namespace std; 15 typedef long long LL; 16 typedef pair<string, string> pr; 17 18 int p, k, s; 19 const int maxn = 205; 20 string str; 21 string word[10]; 22 int cnt[maxn][maxn]; 23 int dp[maxn][maxn]; 24 25 int main() 26 { 27 scanf("%d%d", &p, &k); 28 for(int i = 0; i < p; i++){ 29 string tmp; 30 cin>>tmp; 31 str += tmp; 32 } 33 scanf("%d", &s); 34 for(int i = 1; i <= s; i++){ 35 cin>>word[i]; 36 } 37 38 int len = str.length(); 39 40 for(int i = len - 1; i >= 0; i--){ 41 for(int j = i; j >= 0; j--){ 42 for(int x = 1; x <= s; x++){ 43 int l = word[x].length(); 44 if(str.substr(j, min(i - j + 1, l)) == word[x]){ 45 cnt[j][i] = cnt[j + 1][i] + 1; 46 break; 47 } 48 else cnt[j][i] = cnt[j + 1][i]; 49 //cout<<str.substr(j, min(i - j + 1, l))<<" "<<cnt[j][i]<<endl; 50 } 51 52 } 53 } 54 55 for(int i = 1; i <= k; i++){ 56 dp[i][i] = dp[i - 1][i - 1] + cnt[i][i]; 57 } 58 for(int i = 0; i < len; i++){ 59 dp[i][1] = cnt[0][i]; 60 for(int j = 2; j <= min(k, i); j++){ 61 for(int d = j - 1; d < i; d++){ 62 dp[i][j] = max(dp[i][j], dp[d][j - 1] + cnt[d + 1][i]); 63 } 64 } 65 } 66 67 printf("%d\n", dp[len - 1][k]); 68 69 return 0; 70 }
来源:https://www.cnblogs.com/wyboooo/p/10851238.html