NOIP 2001统计单词个数 解题报告(记忆化搜索)

在线评测:

http://codevs.cn/problem/1040/


整体思路:

,据说是一道化分型DP的题,,然而感觉我想出来的DP够炸时间炸死好几条命的了,,,于是感觉记忆化搜索可以大量减少没有用的dp。然后就记忆化了,,,不断地把一个区间分成两半,然后去搜,,每次枚举中间点和分成几个,搜下去就好了。。

失误之处:

1、开始我傻到把dfs(i,j,k)直接写成dp(i,j,k),,然后竟然神tmd过了60分,,,,,,我在想是出题人傻还是我更傻,,,

2、后来没想到 在枚举两边分配分成几个的时候需要考虑分配的分成几个需要 》= 这一块的元素总个数。。

3、这道题每次dfs的时候分配的分成几个无需减少1,,,,然而傻叉的我因为前几道题减少1而顺理成章的把这个也减少1了,,,竟然tmd过了80,,再次吐槽数据,,

体会心得:

仔细体会dfs的实际含义,改调用函数,还是直接访问自己想清楚,考虑好特殊情况~

AC代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using  namespace  std;
int  n,p,k,s,dp[210][210][50],lenn;
char  tp[50],ch[300],zd[10][210];
int  dfs( int  x, int  y, int  k)
{
     int  maxh = 0,maxk = 0;
     if  (dp[x][y][k])  return  dp[x][y][k];
     int  maxn = 0;
     for  ( int   i = x;i <= y;i++)
     {
         for  ( int  j = 1;j < k;j++)
         {
             if  (j <= i - x + 1  && (k - j) <= (y - x  - (i - x )))
             {
             
             int  tp = dfs(x,i,j) +dfs(i+1,y,k-j);
             if  (tp > maxn) 
             {
                 maxh = i;
                 maxk = j;
                 maxn = tp;
             }
         }
         }
     }
     dp[x][y][k] = maxn;
     return  maxn;
}
int  check( int  x, int  y)
{
     int  tot = 0;
     for  ( int  i = x;i <= y;i++)
     {
         
         for  ( int  j = 1;j <= s;j++)
         {
             bool  pd =  true ;
             int  clen =  strlen (zd[j]);
             for  ( int  o = 0;o < clen;o++)
             {
                 if  (i + clen - 1 > y)
                 {
                     pd =  false ;
                     break ;
                 }
                 if  (ch[i + o]!= zd[j][o])
                 {
                     pd =  false ;
                     break ;
                     
                 }
             }
             if  (pd) 
             {
                 tot++;
                 break ;
             }
         }
     }
     return  tot;
}
void  init()
{
     for  ( int  i = 0;i < lenn - 1;i++)
     {
         for  ( int  j = i;j < lenn;j++)
         {
             dp[i][j][1] = check(i,j);
         }
     }
}
int  main()
{
     scanf ( "%d" ,&n);
     for  ( int  mi = 1;mi <= n;mi++)
     {
         scanf ( "%d%d" ,&p,&k);
         for  ( int  i = 1;i <= p;i++)
         {
             scanf ( "%s" ,tp);
             for  ( int  j = 0;j < 20;j++)
             {
                 ch[(i - 1) * 20 + j] = tp[j];
             }
         }
         lenn = p * 20;
         scanf ( "%d" ,&s);
         for  ( int  i = 1;i <= s;i++)
             scanf ( "%s" ,zd[i]); 
         memset (dp,0, sizeof (dp));
         init();
         int  aans = dfs(0,lenn - 1,k);
         printf ( "%d\n" ,aans);
 
     }
     return  0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值