hdu 1560 附可以卡掉绝大多数ac代码的test

先贴标题中说的那个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;
}

猜你喜欢

转载自blog.csdn.net/qq_38759433/article/details/80748497