#1 - 2019-4-3 23:02
かがや (障子を開けよ、外の世界は広いぞ~)
有一串正整数数组,每次可以去掉一个数,或一个回文数子串,剩下的数顺序不变组成一个新数组,然后重复上面步骤,最少需要几次可以让数组为空?

目前考虑是分两种情况取最小值的推导式,来万能的bangumi问一下各位有没有更好的思路,欢迎大家讨论(bgm40)(bgm40)(bgm40)

/* 2019年4月4日补充 */
不好意思问题有一点没有描述清楚,稍微补充一下
这里的正整数应该小于10的
举个例子,比如初始数组为{1, 2, 3, 1, 2, 1}
第一次去掉第四个数1,得到{1, 2, 3, 2, 1}
第二次可以一次性去掉这个回文数组,所以最少为两次

目前考虑的转移式 f[i, j]=min(f[t-1] + f[t+1][j], f[i + k, j - k])
其中t是i到j之间,k是从i往后和从j往前最长能够构成回文的长度

一晚上能收到这么多回复有点吃惊,我会好好思考一下大家的想法,再次十分感谢大家(bgm93)
#2 - 2019-4-3 23:20
(已淡出bgm38)
我猜是10次
#2-1 - 2019-4-7 14:29
ydc
并不……就算是123456789123456789123456789都远不止10次(bgm38)
#3 - 2019-4-3 23:28
(。´-д-)
我百度到了一个结果(bgm38) 如果没错的话, 它的答案+1就是你的答案
https://blog.csdn.net/jingsuwen1/article/details/51934277

读题失败, 不是这个(bgm38)
#4 - 2019-4-3 23:28
抛砖引玉:
记忆化搜索,首先开一个哈希表,然后进入search函数:
遍历一遍字符串,以每个index为中心找最长的回文子串,(以及每个index,index+1为中心),删除中间的回文字串后递归调用search函数,返回的值是操作次数,然后这个函数中找操作次数的最小值,记录到哈希表里。
时间/空间复杂度:O(N^2)
#4-1 - 2019-4-3 23:46
君寻
然而删除最长的回文子串未必是每一轮的最优选择,也可能存在删除一个数而在下一轮得到更长的回文子串的情况
#4-2 - 2019-4-3 23:48
苇原雪道
直接把最长回文串全删了不一定是最优解。比如
aaabaaaaaaaaaaaaaaaaaaaaaaaaaa,
直接把后面所有a全删了的话要三次,把aaabaaa删了的话只要两次。
#4-3 - 2019-4-4 11:49
233
苇原雪道 说: 直接把最长回文串全删了不一定是最优解。比如
aaabaaaaaaaaaaaaaaaaaaaaaaaaaa,
直接把后面所有a全删了的话要三次,把aaabaaa删了的话只要两次。
所以两种情况都要搜索一次
删除aaabaaa之后递归调用下这个函数
以及删除后面a之后递归调用下这个函数
分别比较这两种情况的返回值,然后选择最小的那个值+1作为返回值。
#4-4 - 2019-4-4 11:52
233
君寻 说: 然而删除最长的回文子串未必是每一轮的最优选择,也可能存在删除一个数而在下一轮得到更长的回文子串的情况
请看#4-3
#4-5 - 2019-4-4 12:03
君寻
233 说: 请看#4-3
这样的话每一轮都要遍历删除每个单数以及每个回文数的情况,时间复杂度是O(N!),不可行啊
#5 - 2019-4-3 23:32
应该就是这个思路吧,要么删掉一个,要么找右侧的该字符的某个occurrence然后分成两段

忽然发现你说的是正整数,是1,2,3,4,3这种每个数字小于10,还是1,2,34,5,43这种,其中34543算回文,或者1,2,34543这种单个数字算回文
#5-1 - 2019-4-3 23:59
揪卡
dp[i ][j] = min(1+dp[i+1][j], dp[i+1][k-1]+dp[k+1][j]) ?大概n^3……
#5-2 - 2019-4-4 00:27
TargetLocked
揪卡 说: dp[i ][j] = min(1+dp[i+1][j], dp[i+1][k-1]+dp[k+1][j]) ?大概n^3……
看起来十分正确的样子……可能需要判一下k==i+1的情况……
#5-3 - 2019-4-4 00:28
揪卡
TargetLocked 说: 揪卡 说: dp[i ][j] = min(1+dp[i+1][j], dp[i+1][k-1]+dp[k+1][j]) ?大概n^3……看起来十分正确的样子……可能需要判一下k==i+1的情况……
哦哦是的,如果k==i+1就直接规约到1+dp[i+2][j]
#6 - 2019-4-3 23:33
(Nomina nuda tenemus.)
发邮件问助教啊(bgm38)
#7 - 2019-4-3 23:42
(一个纠结的面瘫伪宅)
你确定是每次去掉一个回文子串而不是子序列?不过子序列感觉也不好做
显然是不能直接搞dp的,因为状态数会爆炸

我有个贪心的初步想法:
设原数组为S
反过来思考,设数组 P,初始为空
每次可以在当前数组 P 中某个位置处插入一个回文子串p,得到新的P
重复,最后使得P等于S
这样的话可以每次插入S中的一个最长的子序列,设为s 从S中去掉s来更新S
重复,最后使得S为空

寻找S的最长子序列是个很简单的dp问题就不用说了,问题是贪心不好证明(bgm39)
#7-1 - 2019-4-3 23:57
TargetLocked
如果你的意思是原问题等价于每次删一个回文子序列的话,那好像不太对,因为给定一个删除子序列的方案并不能构造一个删除子串的方案使得步数相等……
#7-2 - 2019-4-3 23:58
windrises
TargetLocked 说: 如果你的意思是原问题等价于每次删一个回文子序列的话,那好像不太对,因为给定一个删除子序列的方案并不能构造一个删除子串的方案使得步数相等……
那不是我的意思
#7-3 - 2019-4-4 00:00
smileandyxu
感觉不对貌似
abacadaeafag,用最长子序列要7次,但顺着删只要6次
#7-4 - 2019-4-4 00:04
windrises
smileandyxu 说: 感觉不对貌似
abacadaeafag,用最长子序列要7次,但顺着删只要6次
1、a a a d a a a
2、a b a a d a a a
3、a b a c a d a a a
4、a b a c a d a e a a
5、a b a c a d a e a f a
6、a b a c a d a e a f a g
#7-5 - 2019-4-4 00:13
揪卡
windrises 说: 1、a a a d a a a
2、a b a a d a a a
3、a b a c a d a a a
4、a b a c a d a e a a
5、a b a c a d a e a ...
我还是不太明白你的方法,abacadaeacab你要怎么做呢?应该是4次吧?
#7-6 - 2019-4-4 00:18
windrises
揪卡 说: 我还是不太明白你的方法,abacadaeacab你要怎么做呢?应该是4次吧?
4次是怎么算的
#7-7 - 2019-4-4 00:20
揪卡
windrises 说: 4次是怎么算的
去掉d,去掉e,去掉bacaaacab,去掉a
#7-8 - 2019-4-4 00:24
windrises
揪卡 说: 去掉d,去掉e,去掉bacaaacab,去掉a
那我可以反过来先加个bacaaacab,然后在其中分别插入dea,也是四次
#7-9 - 2019-4-4 00:24
smileandyxu
windrises 说: 1、a a a d a a a
2、a b a a d a a a
3、a b a c a d a a a
4、a b a c a d a e a a
5、a b a c a d a e a ...
不好意思刚弄错,算掉了中间的d(bgm38)
但个人觉得还是很奇怪,比如
abacadaeafagafd
它有多种拆法,
按照daafaad, aaa, bcegf 一共7次(这回貌似没算错吧?)
但另一边aaaaaaa, dfgfd, bce一共5次,起手长度一样,而且应该不会比7更长的子序列了?
#7-10 - 2019-4-4 00:34
TargetLocked
windrises 说: 那不是我的意思
那么是说删除子序列后分割出的若干串互相独立?如果这样的话,贪心的正确性并没有保证,可以考虑abbxayzyx……
#7-11 - 2019-4-4 00:35
揪卡
windrises 说: 那我可以反过来先加个bacaaacab,然后在其中分别插入dea,也是四次
你要怎么找插入的位置
#7-12 - 2019-4-4 00:36
windrises
smileandyxu 说: 不好意思刚弄错,算掉了中间的d
但个人觉得还是很奇怪,比如
abacadaeafagafd
它有多种拆法,
按照daafaad, aaa, bcegf 一共7次(这回貌似没算错吧?)
但另一边aaaa...
我觉得你说的很有道理,虽然我没去验证你说的这个有没有比7更长的子序列,不过肯定会存在这种类似的问题
所以说贪心往往是有问题的(bgm38)

我觉得如果遇到当前状态有多个长度一样的最长子序列的话,可以来个dfs搜索一下,虽然很暴力,不过我觉得平均情况下应该不会太差

不过这也只是猜想,很可能又会冒出别的问题
#7-13 - 2019-4-4 00:39
windrises
揪卡 说: 你要怎么找插入的位置
在得到最长子序列的同时,也能得到其中每个数在原数组的位置,按原位置插入就行了
#7-14 - 2019-4-4 00:48
Black_tea
windrises 说: 我觉得你说的很有道理,虽然我没去验证你说的这个有没有比7更长的子序列,不过肯定会存在这种类似的问题
所以说贪心往往是有问题的

我觉得如果遇到当前状态有多个长度一样的最长子序列的话,可以来个dfs搜索...
上面提出的例子不太好,最长的不一定是最好的,考虑这个:
(5(1 2 1 7 7 1 2 1)5 4 5 5)
最长为 (5 1 2 1 7 7 1 2 1 5) ,需要额外加入 4,5 5
但是如果改为 (5 5 4 5 5),(1 2 1 7 7 1 2 1),这就只需两次
#7-15 - 2019-4-4 00:53
windrises
Black_tea 说: 上面提出的例子不太好,最长的不一定是最好的,考虑这个:
(5(1 2 1 7 7 1 2 1)5 4 5 5)
最长为 (5 1 2 1 7 7 1 2 1 5) ,需要额外加入 4,5 5
但是如果...
你这个例子也不太好 (bgm38)
其实只需要额外加入5 4 5 也是两次 (bgm38)
#7-16 - 2019-4-4 00:54
windrises
我只是随便说说我yy的猜想,很可能是有问题的,你们不要老是回复我了(bgm38)
#7-17 - 2019-4-4 00:55
Black_tea
windrises 说: 你这个例子也不太好
其实只需要额外加入5 4 5 也是两次
(bgm38) 这是最后一次回复
#8 - 2019-4-3 23:49
(一个纠结的面瘫伪宅)
举个栗子吧
4 5 5 4 1 2 3 4 5 5 4
正确方法应该是先用两次来去掉1 2 3 中的两个数,然后剩下的可以一次去掉,一共需要三次操作
而不是先去掉两个4 5 5 4,然后再分别去掉1 2 3,因为这样需要五次操作

-------------
我上面说的S在第一次去掉s之后就变成分段的多个数组了,之后再求最长子序列都是在每个小段里面求,插入时也是一样
#8-1 - 2019-4-4 02:09
你们是怎么定义子串的?

45541234554的最长回文子串是455414554或者455424554或者455434554。

删掉455414554,剩下23。继续找最长回文子串。

有什么问题?
#8-2 - 2019-4-4 07:57
揪卡
说: 你们是怎么定义子串的?

45541234554的最长回文子串是455414554或者455424554或者455434554。

删掉455414554,剩下23。继续找最长回文子串。

有什么问题...
子串和子序列不一样的,你说的是子序列
#9 - 2019-4-4 02:38
啊,还是你说的,同样长度不同划分剩下的串又有继续划分次数多少
#10 - 2019-4-4 07:56
(B站难民)
楼主能更细致的描述自己的算法吗?比如写一下时间复杂度和dp的公式。要不然不是很好比较呀。
#11 - 2019-4-4 14:06
(BGMのTrinitas<=>婊冈妈<=>补冈妈<=>拜冈妈 三位一体 ...)
是不是玩祖玛崩溃了?(bgm38)
穷举大法好
#12 - 2019-4-4 15:18
问了一个竞赛金牌大佬,他给了一个O(n^4)的解法:
f[i ][j]表示 消除区间[i,j]的代价,目标是f[1][n],边界条件f[i ][i ] = 1
为求解f[i ][j],用下面两种来更新,初始f[i ][j] = 正无穷
1 找到x, y使得[i,i+x-1] 和 [j - y + 1, j]拼一起是一个回文串,用1+f[i+x][j-y]更新f[i ][j],i <= x < y <=j
2.  用f[i ][x] + f[x+1][y] + f[y+1][j]更新f[i ][j],    i <= x < y <=j
#12-1 - 2019-4-7 13:48
橘枳橼
dijkstra
#12-2 - 2019-4-7 14:12
橘枳橼
有一个问题,abcdejjjjjfgfeiiiiidcba 这样的。
所以需要寻找多重存在,那样就是 O(n^3 * 2^n)
#12-3 - 2019-4-7 14:26
橘枳橼
哦,我想错了,那样可以 jjjjjfgf 之后产生,两处错误,1. 中 “用1+f[i+x][j-y]更新f[i ][j]” 应为 “用f[i+x][j-y]更新f[i ][j]”,2. 中不需要拆三段,拆两段即可。
因为保证内部一定有刚被删除的回文串,那么只需要最后删除的那个再多删前后两个就行。
而拆三段一定被拆两段覆盖到,参考 dijkstra 算法。
——
该想法已经从 #12 跑偏,转 #14。
#12-4 - 2019-4-7 14:42
橘枳橼
InQβ 说: 哦,我想错了,那样可以 jjjjjfgf 之后产生,两处错误,1. 中 “用1+f[i+x][j-y]更新f[i ][j]” 应为 “用f[i+x][j-y]更新f[i ][j]”,2. 中不需要拆三...
想了一下可以做出一个 最优 n 最差 n^2 平均 n log n 的,依赖缓存实现 DP 而非发现实现的 DP。
采用 C 那样的非对称子界而非 Pascal 的对称子界(为了实现 0 长度边界)
(define f (memproc (lambda (i j)
  (cond
    [(= i j) 0]
    [(= (1+ i) j) 1]
    [(= (ref s i) (ref s j)) (f (1+ i) (- j 1))]
    [else (min (map (lambda (k) (+ (f i k) (f k j))) (range (1+ i) j)))]))))
#12-5 - 2019-4-7 14:46
ydc
InQβ 说: 想了一下可以做出一个 最优 n 最差 n^2 平均 n^3 的,依赖缓存实现 DP 而非发现实现的 DP。
采用 C 那样的非对称子界而非 Pascal 的对称子界(为了实现 0 长度边界)
(def...
这个算法没法解决“最后一次消除是将之前被分割成多段的回文串给消掉”的情况吧。。举个简单的例子abeca,你的做法应该是先消b,再消c,然后消aea
#12-6 - 2019-4-7 14:47
ydc
InQβ 说: 想了一下可以做出一个 最优 n 最差 n^2 平均 n^3 的,依赖缓存实现 DP 而非发现实现的 DP。
采用 C 那样的非对称子界而非 Pascal 的对称子界(为了实现 0 长度边界)
(def...
但是这种消除策略是没法只通过“找到x, y使得[i,i+x-1] 和 [j - y + 1, j]拼一起是一个回文串,用1+f[i+x][j-y]更新f[i ][j]”所考虑到的
#12-7 - 2019-4-7 15:07
windrises
ydc 说: InQβ 说: 想了一下可以做出一个 最优 n 最差 n^2 平均 n^3 的,依赖缓存实现 DP 而非发现实现的 DP。
采用 C 那样的非对称子界而非 Pascal 的对称子界(为了实现 0 长度...
没有考虑到多段问题+1
楼主发帖的当晚就有人说过这个方法了,不过貌似很快就意识到有问题然后删了
#12-8 - 2019-4-7 15:11
ydc
windrises 说: ydc 说: InQβ 说: 想了一下可以做出一个 最优 n 最差 n^2 平均 n^3 的,依赖缓存实现 DP 而非发现实现的 DP。
采用 C 那样的非对称子界而非 Pascal 的对称子界(为了...
我第一反应也是这个方法(bgm38)
#12-9 - 2019-4-7 15:55
橘枳橼
ydc 说: InQβ 说: 想了一下可以做出一个 最优 n 最差 n^2 平均 n^3 的,依赖缓存实现 DP 而非发现实现的 DP。
采用 C 那样的非对称子界而非 Pascal 的对称子界(为了实现 0 长度...
根本不用解决这个问题,按 DP 做法
首先发现消除 b 需要 1 次(length == 1)
然后发现消除 e 需要 1 次(length == 1)
然后发现消除 be 需要 2 次(=f("b")+f("e"))
然后发现消除 c 需要 1 次(length == 1)
然后发现消除 bec 需要 3 次(=f("be")+f("c"))
然后发现消除 abeca 需要 3 次(=f("bec"))

或者说,按照 breakdown 做法
f("abeca")=f("bec") (因为 'a'=='a')
然后就很容易地一个个去掉就行。
根本没有多段问题。
#12-10 - 2019-4-7 16:02
橘枳橼
ydc 说: InQβ 说: 想了一下可以做出一个 最优 n 最差 n^2 平均 n^3 的,依赖缓存实现 DP 而非发现实现的 DP。
采用 C 那样的非对称子界而非 Pascal 的对称子界(为了实现 0 长度...
https://gist.github.com/no1xsyzy/9f10ccc3f90388e93b3317027be5f44c 添加了样例 #12-5
#12-11 - 2019-4-7 16:10
橘枳橼
windrises 说: ydc 说: InQβ 说: 想了一下可以做出一个 最优 n 最差 n^2 平均 n^3 的,依赖缓存实现 DP 而非发现实现的 DP。
采用 C 那样的非对称子界而非 Pascal 的对称子界(为了...
测试过没有问题 https://gist.github.com/no1xsyzy/9f10ccc3f90388e93b3317027be5f44c
楼上的样例都加进去了。
我的想法已经从 #12 跑偏,请看 #14
#13 - 2019-4-7 14:38
想不出多项式算法,只能想出指数级别的暴力。
dp[j]表示把串[i,j]给干掉的最少次数,我们考虑干掉[i,j]的最后一次操作,他肯定是取出[i,j]这段区间里的某些字符,这些字符按顺序拼起来形成一个回文串。当然这些字符不一定连续,所以你把他们给拿掉,就能把[i,j]分成若干段。
由于你刚才取走的数是最后一次操作取的,所以这些段在消掉时肯定彼此独立,就把这些段的花费加起来再加1来更新dp[j]即可
#14 - 2019-4-7 15:50
(我只知道自己一无所知。)
https://gist.github.com/no1xsyzy/9f10ccc3f90388e93b3317027be5f44c

想了想,这个应该是最好的了。
别被 “回文” 骗了,我的代码里根本没有寻找回文串的过程,也没有判断回文串的过程,甚至到最后也不知道是否存在回文串。
只用到一个特性:任何一个回文串前后各加同一个字符还是一个回文串。
因为是 equal? 所以对 (listof any/c) 有效。

顺便把测试用例加上了。
#14-1 - 2019-4-7 16:18
橘枳橼
其实标准的是这个版本
解释如下:
if 长度 = 0,then 需要删除次数 = 0;
elseif 长度 = 1,then 需要删除次数 = 1 次;
elseif 长度 = 2,且两个字符相同,then 需要删除次数 = 1;
elseif 首尾两个字符相同,then 需要删除次数 = 去掉这首尾两个字符后的需要删除次数。
else 需要删除次数 = 分成两段分别求需要删除次数并相加,求其中的最小值。
#14-2 - 2019-4-7 16:20
橘枳橼
符合要求并且复杂度为 O(n^2)。如若不服请给反例。
改写成正常的 DP 不会花太多功夫的,记得优先求 j-i 比较小的。
#14-3 - 2019-4-7 16:25
ydc
哦哦哦,我觉得您说的很对(bgm38)
#15 - 2019-4-7 16:52
(一个纠结的面瘫伪宅)
自言自语,没有仔细思考瞎胡说的,请不要回复我(bgm38)

之所以我一开始就在7楼说不能直接来dp,状态会爆炸,是因为:
不能简单的用dp[i][j]来表示消除i~j子串所需要的次数
我觉得得用dp[i][j][s]表示处理完i~j子串最后剩余s时,所需要的次数 原因就不说了
然后问题在于枚举s不是多项式复杂度