9.12算法

八皇后(数组改良版,不T)(dfs,剪枝)


#include
#include
#include
#include
#include
using namespace std;
const int N = 15;
int arr[N],rs[2*N],ls[2*N],used[N];
int n,res=0;
//bool check(int i, int j) {//尝试在arr[i]放j,即在i,j处放位置
//	for (int k = 1; k < i; k++) {
//		if (arr[k] == j) {//第j列用过了,再用就会一列有两个数
//			return false;
//		}
//		if ((k - arr[k] == i - j) || (k + arr[k] == i + j)) {
//			return false;
//		}
//	}
//	return true;
//}//由于是行数组,所以不用担心一行有两个,即一行(一个数组格子)放不下两个数,只能放一个数
//每次都检查,会T掉

void dfs(int i) {
	if (i == n + 1) {
		if (res < 3) {
			for (int j = 1; j <= n; j++) {
				cout << arr[j] << " ";
			}
			cout << endl;
		}
		res++;
		return;
	}
	for (int j = 1; j <= n; j++) {
		if (used[j] || rs[i + j] || ls[i - j + n]) {
			continue;
		}
		used[j] = 1, rs[i + j] = 1, ls[i - j + n] = 1;
		arr[i] = j;
		dfs(i + 1);
		used[j] = 0, rs[i + j] = 0, ls[i - j + n] = 0;
	}
}
int main() {
	cin >> n;
	dfs(1);
	cout << res;
	return 0;
}

单词接龙(dfs)

输入的第一行为一个单独的整数 �n 表示单词数,以下 �n 行每行有一个单词,输入的最后一行为一个单个字符,表示“龙”开头的字母。你可以假定以此字母开头的“龙”一定存在。

输出格式

只需输出以此字母开头的最长的“龙”的长度。

输入输出样例

5
at
touch
cheat
choose
tact
a
23

用vector string数组接收输入

用哈希表记录每个单词的出现次数

?怎么拼接?

        ?怎么检查单词最后一位?

由龙头往后接,不断更新尾巴,然后从单词数组头往后扫,直到都用过了,或没有了,就和已有答案比较一下并更新

int use[20], length = 0, n;
string str[20];
int canlink(string str1, string str2) {//不断返回两个单词的最短拼接长度,其实可以用一个二维数组来保存
	//以减少调用次数
	for (int i = 1; i < min(str1.length(), str2.length()); i++) {//重叠长度从1开始增加
		int flag = 1;
		for (int j = 0; j < i; j++) {//j是对接的位数
			if (str1[str1.length() - i + j] != str2[j]) {//第一个字符串从尾部,第二个从头部
				flag = 0;
			}
		}
		if (flag)return i;
	}
	return 0;
}
void solve(string strnow, int lengthnow) {
	length = max(lengthnow, length);
	for (int i = 0; i < n; i++) {//从头开始扫单词数组
		if (use[i] >= 2) { continue; }//如果这个单词用过两次就跳过
		int c = canlink(strnow, str[i]);//得到与上个单词的最短拼接长度
		if (c > 0) {//只有拼接得上才递归向下继续拼接
			use[i]++;//拼接后,最后的单词依然是这个拼接的单词,即只看最后的部分,不管整体,只是更新整体的长度
			solve(str[i], lengthnow + str[i].length() - c);//拼接上了第i个单词,那么末尾就是第i个单词和其他单词拼
			use[i]--;
		}
	}
}
cin >> n;
for (int i = 0; i <= n; i++) {
	cin >> str[i];
}
solve(' ' + str[n], 1);
cout << length;

如touchc,chceat,重叠部分c,ch,chc,要让最长,就要重叠部分最小,拼接起来就应该是touchchceat,而不应该继续重叠,所以就要让拼接长度从小到大,即如果一旦拼接得上,就立即终止继续增长拼接长度,这样就能保证拼接长度是最短的

而如果要让拼接长度最大,就需要让拼接长度从大到小遍历,即如果一旦拼接得上,就立即终止继续缩短拼接长度

//yc[i][j]来存储第i个单词后连接第j个单词的最小重叠部分(mt函数)
//预处理
//预处理完后是深搜dfs
//
//
string tr[30][30];

int mt(int x, int y) {//返回x单词后连接一个y单词的最小重叠部分
	//x单词从后往前,y单词从前往后
	for (int k = tr[x].size() - 1; k >= 0; k--) {//从x单词尾部向前看看最小重叠部分是从哪开始
		for (int kx = k; kx < tr[x].size(); kx++) {//检测k到末尾
			if (tr[x][kx] != tr[y][ky++]) {
				pp = false;//不匹配
				break;
			}
		}
		if(pp=true){//都相等,匹配,直接返回
			return tr[x].size() - k;
		}
		ky = 0;
		pp = true;
	}
	return 0;
}
void dfs(int p) {
	bool jx = false;
	for (int j = 1; j <= n; j++) {
		if (vis[j] >= 2)continue;
		if (yc[p][j] == 0)continue;//不能拼上
		if (yc[p][j] == tr[p].size() || yc[p][j] == tr[j].size())continue;//存在包含关系
		an += tr[j].size() - yc[p][j];
		vis[j]++;
		jx = true;
		dfs(j);
		an -= tr[j].size() - yc[p][j];
		vis[j]--;
	}
	if (jx == false) {
		ans = max(ans, an);
	}
	return;
}
for (int i = 1; i <= n; i++) {
	for (int j = 1; j <= n; j++) {
		yc[i][j] = mt(i, j);//预处理
	}
}
for (int i = 1; i <= n; i++) {
	if (tr[i][0] == ch) {//从头到尾看一下有没有指定的字母开头
		vis[i]++;
		an = tr[i].size();
		dfs(i);//以这个字母为开头,覆盖掉了指定的字符
		vis[i] = 0;
	}
}

最大称量的重量(dfs,剪枝)

并且,这一行中从第 3个砝码开始,每个砝码的质量至少等于前面两个砝码(也就是质量比它小的砝码中质量最大的两个)的质量的和。

就是说从第三个开始,至少大于斐波那契的增长,而斐波那契40项左右就过2^30了,所以实际可用范围在50之内

//
/*
dfs(long long cur) {//cur表当前重量
	if (cur + b[index] <= maxc) {//b为前缀和,如果当前重量加上Index前面所有砝码都不过max,就return
		return;
	}
	maxc = max(cur, maxc);
}*/
long long  sum[50], a[50];
int c, n;
long long ans = 0;
void dfs(int cur, long long x)
{
	if (x > c)return;
	if (sum[cur - 1] + x <= c)
		//一个剪枝:如果前面那些砝码可以全部取走,那直接取走即可。
	{
		ans = max(ans, sum[cur - 1] + x);
		return;
	}
	ans = max(ans, x);
	for (int i = 1; i < cur; i++)
		dfs(i, x + a[i]);
	return;
}
cin >> n >> c;
for (int i = 1; i <= n; i++) {
	cin >> a[i];
	sum[i] = sum[i - 1] + a[i];
}
dfs(n + 1, 0);
cout << ans << endl;

max()函数中必须是同类型,即都为Longlong,不能一个Int,一个Longlong 

背包的思路可以用,但是不能用背包的解法,因为最大到了2^30,数组装不下

奇迹5440(闰年,质数,年月处理)

1.倒数两位,四位,整个都构成质数

2/检测日期是否真实存在(闰年,月份,头疼)


#include
#include
#include
#include
#include
using namespace std;
const int p[] = { 0,3,5,7,11,13,17,19,23,29,31 };//这是每个月里的质数日,提前做出检验
//一定要注意这个数组最后一位要放一个比d数组大的数,目的是终止循环,如果让31作为最后一个数字,那么在到31时,还要继续往后走,但后面已经没有数了
//所以需要一个大于任何d数组中的数作为结尾,来终止对p数组的访问
//或者在循环终止条件中写入j<=这个数组长度
const int d[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
int T, a[66], t, ans[66666], tot;
char s[10];
inline bool is_prime(int x) {
	for (int i = 2; i * i <= x; i++) {
		if (x % i == 0) {
			return false;
		}
	}
	return true;
}
int main() {
	ios::sync_with_stdio(0);
	for (int i = 1; i <= 12; i++) {//i表示月份,开始组合月日
		for (int j = 1; p[j] <= d[i]&&j<=10; j++) {//要保证每个月里的质数日不超过当月的限制,然后遍历j
			if (is_prime(i * 100 + p[j])) {
				a[++t] = i * 100 + p[j];//之所以是前置++,是让t还能起到计数的作用,还能填数
				//即t初始为0,先是t++,t=1,(相当于有数了,然后cnt++),再在一号位置上填数
			}
		}
	}
	for (int i = 4; i <= 9999; i += 4) {//四年一闰
		if ((i % 100 || !(i % 400)) && is_prime(i * 10000 + 229)) {//i%100表示如果能除尽,即为0,不闰,不判断
			//不然就不为0,做出判断,即组合起来的时间得是质数
			ans[++tot] = i * 10000 + 229;
		}
	}//特判闰年,四年一闰,百年不闰,400年又闰
	//闰年特殊就特殊在二月多一天,所以考虑闰年主要就是考虑这多出来的一天229
	for (int i = 1; i <= 9999; i++) {
		for (int j = 1; j <= t; j++) {//j是已经组合好的质数日月,再和年组合,判断是不是质数
			if (is_prime(i * 10000 + a[j])) {
				ans[++tot] = i * 10000 + a[j];
			}
		}
	}
	//至此得到所有的质数时间
	cin >> T;
	while (T--) {
		cin >> (s + 1);
		int cnt = 0;
		for (int i = 1; i <= tot; i++) {//从所有的质数时间中开始检验
			int now = ans[i], flag = 1;
			for (int j = 8; flag && j; j--, now /= 10) {//循环条件为flag保持与尚有存为
				if (s[j] != '-' && s[j] - '0' != now % 10) {//now%10得到当前的最后一位数
					//可以匹配上的条件为当前位是-,即未知
						//或者当前位和当前的质数时间的对应位置数字相同
						//只有满足匹配条件才可继续匹配,直到匹配完成是可能的
						//或者不匹配,匹配不上,试试下一个质数时间
					flag = 0;
				}
			}
			cnt += flag;//加上检测过的flag
		}
		cout << cnt << endl;
	}
	return 0;
}

油滴扩展1378(dfs)

1.怎么判断停止扩散

A:碰壁或者超过已扩散油滴所允许的最大半径

2.油滴滴后怎么对其他油滴造成影响?怎么写?

A:通过数组标记已扩展的油滴,在后续油滴检测时发挥作用

3.怎么确定顺序,找max?

即确定半径的顺序

A:如果有方法提前确定,那就是贪心策略;

如果没有,那就是搜索,即列举出所有情况,然后选最优。

const double  pi = 3.1415926535;
bool s[10];
double x[10], y[10], r[10], xa, xb, ya, yb, ansmax;
int n;
double cal(int i) {
	double s1 = min(abs(x[i] - xa), abs(x[i] - xb));
	double s2 = min(abs(y[i] - ya), abs(y[i] - yb));
	double ans = min(s1, s2);
	for (int j = 1; j <= n; j++) {
		if (i != j && s[j]) {//从头开始遍历,只有不是本身以及油滴已经扩展过了,才进行判断
			//否则如果没扩展过,就没r,自然也判断不了
			double d = sqrt((x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j]));
			ans = min(ans, max(d - r[j], 0.0));//d-r[j]是这个点的油滴相对于j油滴所能允许扩展的最大半径
		}
	}
	return ans;
}
void dfs(int k, double sum) {
	if (k > n) {
		ansmax = max(ansmax, sum);
		return;
	}
	for (int i = 1; i <= n; i++) {
		if (!s[i]) {
			r[i] = cal(i);
			s[i] = 1;//通过标记s数组,对后续的油滴半径确定造成影响
			dfs(k + 1, sum + r[i] * r[i] * pi);
			s[i] = 0;
		}
	}
}
double ss;
cin >> n;
cin >> xa >> ya >> xb >> yb;
ss = abs(xa - xb) * abs(ya - yb);
for (int i = 1; i <= n; i++) {
	cin >> x[i] >> y[i];
}
dfs(1, 0);
cout << int(ss - ansmax + 0.5);//四舍五入的好方法

四舍五入

填涂颜色1162(遍历)

1.怎么识别闭合圈?

A:用两个数组实现,一个是原数组数据,不要动

一个是用来修改1之外的0,修改通过搜索的方式(dfs,bfs),遇到0就修改为1,直到周围全是1

这样,对于1围起来的0就修改不了,因为根本访问不到里面,而只能把能访问到的(即1之外的)0全修改成1

2.对于1围起来的0和1之外的0怎么区分?

A:通过另一个数组

3.怎么修改1围起来的0?

A:遍历另一个数组,如果访问的位置依然是0,就说明这是1围起来的0,没有被修改过,这时就要输出2,其他情况一律输出原数组的原数据

void dfs(int p, int q) {
	a[p][q] = 1;
	for (int i = 1; i <= 4; i++) {
		int newp = p + dx[i], newq = q + dy[i];
		if (newp >= 1 && newp <= n && newq >= 1 && newq <= n && a[p][q] == 0) {
			dfs(newp, newq);
		}
	}
}
for (int i = 1; i <= n; i++) {
	for (int j = 1; j <= n; j++) {
		cin >> b[i][j];
		if (b[i][j] == 0) {
			a[i][j] = 0;
		}
		else {
			a[i][j] = 1;
		}
	}
}
for (int i = 1; i <= n; i++) {
	for (int j = 1; j <= n; j++) {
		if (a[i][j] == 0) {
			cout << 2 << " ";
		}
		else {
			cout << b[i][j] << " ";
		}
	}
	cout << endl;
}

马的遍历1443

想法:计一个方向数组,然后从初始点,不断往后走,用cnt记录当前步数,并把这个步数和当前位置上的数作比较取min,最后输出就行

用bfs,有天然优势

用dfs,会爆栈,没找到问题与解决方法


#include
#include
#include
#include
#include
#include
using namespace std;
int dx[8] = { 2,1,-1,-2,-2,-1,1,2 }, dy[8] = { 1,2,2,1,-1,-2,-2,-1 };
int cnt = 0,m,n,x,y;
int map[405][405];
struct node {
	int x, y;
	node(int a, int b) :x(a), y(b) {};
};
//void dfs(int x, int y) {
//	map[x][y] = cnt;
//	for (int i = 0; i < 8; i++) {
//		int newx = x + dx[i], newy = y + dy[i];
//		if (newx >= 1 && newx <= n && newy >= 1 && newy <= m&& (cnt + 1 < max(map[newx][newy], -1)) || map[newx][newy] == -1) {
//			cnt++;
//			dfs(newx, newy);
//			cnt--;
//		}
//	}
//}
//cin >> m >> n >> x >> y;
//memset(map, -1, sizeof(map));
//dfs(x, y);
//for (int i = 1; i <= n; i++) {
//	for (int j = 1; j <= m; j++) {
//		cout << map[i][j] << " ";
//	}
//	cout << endl;
//}
bool use[405][405];
int main() {
	cin >> n >> m;
	cin >> x >> y;
	queueq;
	q.push(node(x, y));
	use[x][y] = 1;
	while (!q.empty()) {
		int num = q.size();
		for (int k = 1; k <= num; k++) {
			node cur = q.front();
			q.pop();
			map[cur.x][cur.y] = cnt;
			for (int i = 0; i < 8; i++) {
				int newx = cur.x + dx[i], newy = cur.y + dy[i];
				if (newx >= 1 && newx <= n && newy >= 1 && newy <= m && !use[newx][newy]) {
					q.push(node(newx, newy));
					use[newx][newy] = 1;
				}
			}
		}
		
		cnt++;
		
	}
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			if (use[i][j]) { cout << map[i][j] << " "; }
			else { cout << -1 << " "; }
		}
		cout << endl;
	}
	return 0;
}

你可能感兴趣的:(算法,c++,深度优先)