先贴标题中说的那个test:
1
4
AAAAA
TTTTT
CCCCC
GGGGG
顺便吐槽下hdu,数据确实有点水,这要是在CF,早就被hack上天了
再记录下自己对迭代加深的一点新的体会:
bfs虽然可以找到最优解,但是对于它访问的每一个状态,它们的结构是固定的,比如对于字符串,它们的长度相等
dfs虽然可以通过传递参数的方式很方便地更改每个状态的结构,但是它不是最优解
这样,迭代加深搜索这个博采众家之长的算法应运而生
以前的我没有明确地意识到ida*也有像a*一样的h函数,是这道题让我意识到自己以前做的那种基于深度的剪枝其实就是h函数,(深度是f(x), 已经确定的字符数量是g(x), 估计的那个量是h(x))也正是因为有了它,ida*才称之为ida*而不是迭代加深搜索。这道题还给了我一个debug的思路:当ida*或a*算法超时时,想办法优化h函数!
下面开始讲题,讲题的过程其实也是我不断探索的过程,大家将会看到三份代码,第一份超时,第二份可以ac但不能过掉开头我给出的test,第三份ac并且可以过掉那个test
拿到问题,求最小值,状态会发生变化并且变化的不复杂,就可以确定是ida*了,每一个位置枚举ATCG四种情况,这样,这个算法的轮廓就出来了,剩下的就是如何设计用来剪枝的h函数了。我在这道题探索的过程其实就是对h函数探索的过程。
第一份,预处理求出串的总长度,然后记录已匹配的长度,求出还能增加几个字符记为x,理想化每个x都能消除4个字符,那么剪枝的表达式自然就是:sum > num + n * (ans - cnt)。当然,看一下就知道,h函数太宽泛了,那时候还没有清楚地意识到h函数的概念,所以没想到从h函数去优化,而是在别处加了各种优化,比如那个启发式的地方,优先选择数量多的字符消除,自然是不可能过掉的。
#include <bits/stdc++.h> using namespace std; char dir[4] = {'A', 'C', 'G', 'T'}; int pos[10]; string s[10]; bool ok; int ans; int sum; int n; char debug[100]; struct data{ int val, id; }; bool cmp(data x, data y){ return x. val > y. val; } void dfs(int cnt, int num){ if(ok) return ; if(cnt == ans){ if(num == sum){ ok = true; } return ; } if(sum > num + n * (ans - cnt)) return ; bool rem[10]; data x[4]; for(int i = 0; i < 4; i ++) x[i] = {0, i}; //启发式,按匹配的字母的数目做优先级排序 for(int i = 0; i < n; i ++){ if(pos[i] < s[i]. size()){ if(s[i][pos[i]] == 'A') x[0]. val ++; if(s[i][pos[i]] == 'C') x[1]. val ++; if(s[i][pos[i]] == 'G') x[2]. val ++; if(s[i][pos[i]] == 'T') x[3]. val ++; } } sort(x, x + 4, cmp); for(int k = 0; k < 4; k ++){ if(x[k]. val == 0) break; memset(rem, false, sizeof(rem)); for(int i = 0; i < n; i ++){ if(pos[i] < s[i]. size() && dir[x[k]. id] == s[i][pos[i]]){ rem[i] = true; pos[i] ++; } } dfs(cnt + 1, num + x[k]. val); for(int i = 0; i < n; i ++){ if(rem[i]) pos[i] --; } } } int main(){ int t; cin >> t; while(t --){ sum = 0; ok = false; cin >> n; ans = 0; for(int i = 0; i < n; i ++){ cin >> s[i]; int x = s[i]. size(); sum += x; ans = max(x, ans); } ans --; while(ok == false){ memset(pos, 0, sizeof(pos)); ans ++; dfs(0, 0); } cout << ans << endl; } return 0; }
然后,借鉴了别人的思路后,写了新的h函数的求法:所有串中未完成匹配的串剩余字母的最大值作为h值,显然,要比上一个好多了,ac了
#include <bits/stdc++.h> using namespace std; char dir[4] = {'A', 'C', 'G', 'T'}; int pos[10]; string s[10]; bool ok, tag; int ans; int sum; int n; struct data{ int val, id; }; bool cmp(data x, data y){ return x. val > y. val; } void dfs(int cnt, int num){ if(ok) return ; if(cnt == ans){ if(num == sum){ ok = true; } return ; } int temp = 0; for(int i = 0; i < n; i ++){ int xx = s[i]. size(); temp = max(temp, xx - pos[i]); } if(temp + cnt > ans) return ; /* if(sum > num + n * (ans - cnt)){ return ; } */ bool rem[10]; data x[4]; for(int i = 0; i < 4; i ++) x[i] = {0, i}; //启发式,按匹配的字母的数目做优先级排序 for(int i = 0; i < n; i ++){ if(pos[i] < s[i]. size()){ if(s[i][pos[i]] == 'A') x[0]. val ++; if(s[i][pos[i]] == 'C') x[1]. val ++; if(s[i][pos[i]] == 'G') x[2]. val ++; if(s[i][pos[i]] == 'T') x[3]. val ++; } } sort(x, x + 4, cmp); for(int k = 0; k < 4; k ++){ if(x[k]. val == 0){ break; } memset(rem, false, sizeof(rem)); for(int i = 0; i < n; i ++){ if(pos[i] < s[i]. size() && dir[x[k]. id] == s[i][pos[i]]){ rem[i] = true; pos[i] ++; } } dfs(cnt + 1, num + x[k]. val); for(int i = 0; i < n; i ++){ if(rem[i]) pos[i] --; } } } int main(){ int t; cin >> t; while(t --){ sum = 0; ok = false; cin >> n; ans = 0; for(int i = 0; i < n; i ++){ cin >> s[i]; int x = s[i]. size(); sum += x; ans = max(x, ans); } ans --; while(ok == false){ memset(pos, 0, sizeof(pos)); tag = false; ans ++; dfs(0, 0); } cout << ans << endl; } return 0; }
注意第二个h函数,模糊了每个字母是什么的特征,只关系它们的数量,第三份就从这入手,求出每个串需要的各个字母的数量,然后把4个字母中数量的最大值加起来作为h值。顺利解决开头给出的test
#include <bits/stdc++.h> using namespace std; char dir[4] = {'A', 'C', 'G', 'T'}; int pos[10]; string s[10]; int h[10][4]; bool ok, tag; int ans; int sum; int n; struct data{ int val, id; }; bool cmp(data x, data y){ return x. val > y. val; } void dfs(int cnt, int num){ if(ok) return ; if(cnt == ans){ if(num == sum){ ok = true; } return ; } memset(h, 0, sizeof(h)); for(int i = 0; i < n; i ++){ for(int j = pos[i]; j < s[i]. size(); j ++){ if(s[i][j] == 'A') h[i][0] ++; if(s[i][j] == 'C') h[i][1] ++; if(s[i][j] == 'G') h[i][2] ++; if(s[i][j] == 'T') h[i][3] ++; } } int temp = 0; for(int j = 0; j < 4; j ++){ int x = 0; for(int i = 0; i < n; i ++){ x = max(x, h[i][j]); } temp += x; } if(temp + cnt > ans) return ; /*int temp = 0; for(int i = 0; i < n; i ++){ int xx = s[i]. size(); temp = max(temp, xx - pos[i]); } if(temp + cnt > ans) return ;*/ /* if(sum > num + n * (ans - cnt)){ return ; } */ bool rem[10]; data x[4]; for(int i = 0; i < 4; i ++) x[i] = {0, i}; //启发式,按匹配的字母的数目做优先级排序 for(int i = 0; i < n; i ++){ if(pos[i] < s[i]. size()){ if(s[i][pos[i]] == 'A') x[0]. val ++; if(s[i][pos[i]] == 'C') x[1]. val ++; if(s[i][pos[i]] == 'G') x[2]. val ++; if(s[i][pos[i]] == 'T') x[3]. val ++; } } sort(x, x + 4, cmp); for(int k = 0; k < 4; k ++){ if(x[k]. val == 0){ break; } memset(rem, false, sizeof(rem)); for(int i = 0; i < n; i ++){ if(pos[i] < s[i]. size() && dir[x[k]. id] == s[i][pos[i]]){ rem[i] = true; pos[i] ++; } } dfs(cnt + 1, num + x[k]. val); for(int i = 0; i < n; i ++){ if(rem[i]) pos[i] --; } } } int main(){ int t; cin >> t; while(t --){ sum = 0; ok = false; cin >> n; ans = 0; for(int i = 0; i < n; i ++){ cin >> s[i]; int x = s[i]. size(); sum += x; ans = max(x, ans); } ans --; while(ok == false){ memset(pos, 0, sizeof(pos)); tag = false; ans ++; dfs(0, 0); } cout << ans << endl; } return 0; }