HDU 5677

这个问题可以先处理处一个数组A来

其中A[i] 代表长度为i的回文串有多少个。

那么定义状态d[i][j]代表当前已经选了i个总长度为j这个状态是否可达。问题就是个多重背包。


但是当时做的时候采用了另一个思路,定义d[ i ] [ j ] [ k ] 代表当前只考虑到长度为i 及以上的 回文串选择情况时,当前还剩余j个没有选,需要这j个总长度为k;

那么,最普通的转移就是o(n)就是枚举i长度的选了几个,然后转移到i+1对应的状态,并且这样转移没有办法优化,


那么,我们这样来考虑问题当前还有j个串没有选,不管这一次选了几个长度为i的串,这k个串的长度都将>=i , 我们可以预先将着k个长度都减去1,因为这个1迟早会交付。


所以不管这一次选了几个当前长度的回文串,转移必将如此, d[ i ][ j ][k] = d[i][j][k] | ( d[i + 1][ j - x ] [ k - j ])( x为这次选了几个长度为i的回文串), 那么转移到的状态是第三维恒为定值的一个连续区间,那么就可以通过预处理i + 1 状态的区间和,来更新i的状态,做到O(1)的转移

  

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long ll;
#define rep(i,n) for(int i =0;i<(int)n;i++)
#define rep1(i , x, y) for(int i = (int)x;i<=(int)y;i++)

const int N = 105;
char s[N];
int a[N];
int d[N][N];
int max_ = 0;
void cal(){
   int n = strlen(s);
   for(int len = 1 ; len <= n ; len ++)
      for(int i = 0; i + len <= n; i++){
          int j = i + len - 1;
          if(len == 1)
               d[i][j] = 1;
          else if(len == 2){
               d[i][j] = (s[i] == s[j]);
          }  else {
              d[i][j] = ((s[i] == s[j]) & d[i + 1][j - 1]);
          }
          if(d[i][j]){
            a[len]++;
            max_ = max(max_ , len);
          }
   }
}
int n , K , L;
bool d2[N][N][N];
int main()
{
   int T;
   scanf("%d",&T);
   while(T--){
       scanf("%d %d %d",&n ,&K , &L);
       memset(a , 0, sizeof(a));
       max_ = 0;
       for(int i =1 ; i<=n;i++){
           scanf("%s",s);
           cal();
       }
       int sum[N][N];
       for(int i = max_ + 1 ; i >= 1; i--){
           for(int j = 0 ; j<= K; j++)
                for(int k =0 ; k <= L; k++){
                      if(i == max_ + 1){
                           if(j == 0 && k == 0)
                                  d2[i][j][k] = true;
                           else d2[i][j][k] = false;
                      }  else {
                           if(j > k)
                                d2[i][j][k] = false;
                           else {
                               int l = max(0 , j - a[i]) , r= j;
                               int all = sum[k - j ][r] - (l ==0 ? 0 : sum[k - j][l - 1]);
                               d2[i][j][k] = (all > 0);
                           }
                      }
                }
            memset(sum , 0, sizeof(sum));
            for(int k = 0 ; k<=L; k++)
               for(int j = 0 ; j<=K; j++)
                     sum[k][j] += (j-1 < 0 ? 0 :sum[k][j-1]) + d2[i][j][k];
       }
       printf("%s\n",d2[1][K][L] ? "True" : "False");
   }
   return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值