HDU 4295 4 substrings problem(状态压缩DP)

题意:

给定一个母串S,和S的子串a,b,c,d。求子串能最多(最少)覆盖母串的多少字符?每个子串必须用上,且只能使用一次。

Sample:

hello

he

l

l

o

答案为:4 6

 

 

分析:

每个子串只能使用一次,且全部用上,集合状态,状态压缩。然后动态规划:

dp[i][j][k]表示S串里考虑到第i个字符,字符串集合状态为k(二进制状态压缩),从当前第i个字符已经向后覆盖了j个字符的最大(小)覆盖数。初始dp[i][0][0] = 0;

状态转移:对于当前状态dp[i][j][k],枚举每一个字符串p,其不在集合k中,如果它能和母串S从当前第i个字符开始匹配,那么考虑把它放在这里,状态转移到i+1阶段,dp[i+1][nj][cur],nj = max(j,len[p]),cur = k | (1<<p)。

 

  1 #include<stdio.h>
  2 #include<string.h>
  3 #include<math.h>
  4 #include<algorithm>
  5 #define maxn 5000
  6 
  7 using namespace std;
  8 char S[maxn];
  9 char str[70];
 10 int len[5];
 11 int match[maxn][5];
 12 int n;
 13 int m;
 14 bool check(int i)
 15 {
 16     int j = 1;
 17     while (i<=n && j<=m)
 18     {
 19         if (S[i-1] != str[j-1])
 20             return false;
 21         i++;
 22         j++;
 23     }
 24     if (j==m+1)
 25         return true;
 26     return false;
 27 }
 28 void KMP(int k)
 29 {
 30     m = strlen(str);
 31     len[k] = m;
 32     for (int i=1;i<=n;i++){
 33         if (check(i)){
 34             match[i][k] = 1;
 35             //printf("%d %d\n",i,k);
 36         }
 37     }
 38     return ;
 39 }
 40 
 41 
 42 int dp[maxn][70][16];
 43 int DP_max()
 44 {
 45     int ans = 0;
 46     memset(dp,-1,sizeof(dp));
 47     for (int i=1;i<=n;i++)
 48     {
 49         dp[i][0][0] = 0;
 50         for (int j=0;j<=64;j++)
 51         {
 52             for (int k=0;k<=15;k++)
 53                 if (dp[i][j][k]>=0){
 54                     int nj = j>0?j-1:0;
 55                     dp[i+1][nj][k] = max(dp[i+1][nj][k],dp[i][j][k]);
 56                     for (int p = 1;p<=4;p++){
 57                         int tmp = 1<<(p-1);
 58                         if ((k&tmp)==0 && match[i][p]){
 59                             nj = max(j,len[p]);
 60                             dp[i][nj][k|tmp] = max(dp[i][nj][k|tmp],dp[i][j][k] + nj - j);
 61                         }
 62                     }
 63                 }
 64             ans = max(ans,dp[i][j][15]);
 65         }
 66     }
 67     return ans;
 68 }
 69 int DP_min()
 70 {
 71     int inf = 1000000;
 72     int ans = inf;
 73     for (int i=1;i<=n;i++)
 74         for (int j=0;j<=64;j++)
 75             for (int k=0;k<=15;k++)
 76                 dp[i][j][k] = inf;
 77     for (int i=1;i<=n;i++)
 78     {
 79         dp[i][0][0] = 0;
 80         for (int j=0;j<=64;j++)
 81         {
 82             for (int k=0;k<=15;k++){
 83                 if (dp[i][j][k] < inf){
 84                     int nj = j>0?j-1:0;
 85 
 86                     dp[i+1][nj][k] = min(dp[i+1][nj][k],dp[i][j][k]);
 87                     for (int p=1;p<=4;p++){
 88                         int tmp = 1<<(p-1);
 89                         if ((k&tmp)==0 && match[i][p]){
 90                             nj = max(j,len[p]);
 91                             dp[i][nj][k|tmp] = min(dp[i][nj][k|tmp],dp[i][j][k]+nj-j);
 92                         }
 93                     }
 94                 }
 95             }
 96             ans = min(ans,dp[i][j][15]);
 97         }
 98     }
 99     return ans;
100 }
101 int main()
102 {
103     while (scanf("%s",S)==1){
104         n = strlen(S);
105         memset(match,0,sizeof(match));
106         for (int i=1;i<=4;i++)
107         {
108             scanf("%s",str);
109             KMP(i);
110         }
111 
112         int ans1 = DP_min();
113         int ans2 = DP_max();
114 
115         printf("%d %d\n",ans1,ans2);
116     }
117     return 0;
118 }
hdu4295(状态压缩)

 

转载于:https://www.cnblogs.com/wangsouc/articles/3291538.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值