题单
P1219 [USACO1.5]八皇后 Checker Challenge
思路 :
根据题干,我们可以先放第一行元素,在放第二行的元素,……,最后放第n的元素。这样做的好处是,我们在搜索时可以只枚举列,而不用枚举行。然后我们在定义b[i]、c[i]、d[i]分别来记录第i列、第i个与主对角线平行的对角线、第i个与副对角线平行的对角线上是否有棋子;行是不需要记录的,因为我们是顺次把棋子放下来的。最后,我们用一个a数组来记录下路径就可以了。
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 105;
int n, a[maxn], b[maxn], c[maxn], d[maxn], total;
void print () // 打印路径
{
for (int i = 1; i <= n; i++)
{
printf ("%d ", a[i]);
}
printf ("\n");
}
void dfs (int cur)
{
for (int j = 1; j <= n; j++)
{
if (!b[j] && !c[cur + j] && !d[cur - j + n])
{
a[cur] = j;
if (cur == n)
{
total ++;
if (total <= 3)
print ();
return;
}
b[j] = 1;
c[cur + j] = 1;
d[cur - j + n] = 1;
dfs (cur + 1);
b[j] = 0;
c[cur + j] = 0;
d[cur - j + n] = 0;
}
}
}
int main ()
{
scanf ("%d", &n);
dfs (1);
printf ("%d", total);
return 0;
}
收获
①学会记录并打印路径
②学会如何标记主副对角线
P1019 [NOIP2000 提高组] 单词接龙
思路
首先,为了dfs能够正常进行,我们需要找出可以实现拼接的且不同的字符串对(si, sj),并把他们的拼接(即重合)的最小长度记录在a[i][j]中。为什么是最小长度?因为我们的答案要的是拼接后的串的最大长度,故对于两个可实现拼接的字符串,其用于拼接的长度越短越好。然后就是我们发现每一个串可以用两次,那怎么办,vis[i]只能记录它是否被用过一次,其实并不然,我们只需把vis[i] == 0 改为vis[i] <= 1就可以实现表示其还没用完(而不是还没被用),那题干含说了两个用与拼接的的字符串不能有包含关系,这只需要aij!=si.length()&&aij!=sj.length (),就可以了。
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 50;
string s[maxn];
int n, a[maxn][maxn], ans, vis[maxn];
char ch;
int f (string x, string y) // 返回两个串i串末尾与j串首段的最小重叠长度
{
int lenx = x.length ();
for (int l = 1; l <= lenx; l++) //从1开始
{
int flag = 1;
for (int i = lenx - l, j = 0; i < lenx; i++, j++)
{
if (x[i] != y[j])
{
flag = 0;
break;
}
}
if (flag == 1)
{
return l;
}
}
return 0;
}
void dfs (int cur, int ansi)
{
for (int i = 1; i <= n; i++)
{
if (vis[i] <= 1 && a[cur][i] > 0 && a[cur][i] != s[cur].length () && a[cur][i] != s[i].length ()) // 这里是本题的重点
{
vis[i] ++;
dfs (i, ansi + s[i].length () - a[cur][i]); //容斥原理
vis[i] --;
}
}
//到这了,说明不可拼接了,更新一下ans
ans = max (ansi, ans);
return ;
}
int main ()
{
scanf ("%d", &n);
for (int i = 1; i <= n; i++)
{
cin >> s[i];
}
cin >> ch;
//初始化a[i][j]
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
a[i][j] = f (s[i], s[j]);
a[j][i] = f (s[j], s[i]);
}
}
for (int i = 1; i <= n; i++)
{
if (s[i][0] == ch)
{
vis[i] ++;
dfs (i, s[i].length ());
vis[i] --;
}
}
printf ("%d", ans);
return 0;
}
收获
①打破对vis数组的刻板用法
②学会判断包含关系
P5194 [USACO05DEC]Scales S
思路
这个题n最多有1000个,这显然会超时,而这个题由不可以记忆化,故考虑搜索的剪枝。 首先我们想为啥会TLE,因为一个一个枚举太慢了,那我们应该去寻找一个方法使得使得一次可以加上许多个,于是考虑到可以使用前缀和pre[i]记录前i个砝码的总质量,具体的操作方法见代码说明。当然,如果目前加上去的砝码总质量超过了c,那么一定要return了,这也是一个必要的剪枝。
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1005;
long long a[maxn], n, c, ans;
long long pre[maxn];
void dfs (int cur, long long x) //表示目前下标从后往前加上了的下标最小砝码的是cur,x表示目前的总重量,因为cur是递减的,所以可以做到不重不漏
{
if (x > c) //超过最大称重的一定不合理
{
return;
}
if (x + pre[cur - 1] <= c) //如果前cur-1个可以全加上,那么直接全加上,然后更新答案
{
ans = max (ans, x + pre[cur - 1]);
return;
}
//如果不可以。则先更新一下答案,在从前cur-1个中找一个放上去。
ans = max (ans, x);
for (int i = 1; i < cur; i++)
{
dfs (i, x + a[i]);
}
}
int main ()
{
scanf ("%lld %lld", &n, &c);
for (int i = 1; i <= n; i++)
{
scanf ("%lld", &a[i]);
pre[i] = pre[i - 1] + a[i]; //前缀和数组
}
dfs (n + 1, 0);
printf ("%lld", ans);
return 0;
}
收获
①知道了前缀和也可以用于剪枝
②知道了一种让搜索做到不重不漏的新的搜索方法,详见dfs 函数
P1378 油滴扩展
思路
一个模拟题,但又一个特别需要注意的点,就是内切的情况,其半径要设为0!!!
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100;
int n, xii, yii, xi, yi, x[maxn], y[maxn], vis[maxn], temp[maxn];
double r[maxn], pi = 3.1415926535, ans = 2147483647;
int l (int i, int j)
{
int delx = x[i] - x[j], dely = y[i] - y[j];
return delx * delx + dely * dely;
}
void dfs (int tot, int cur)
{
temp[tot] = cur;
double li = min (abs (x[cur] - xi), abs (x[cur] - xii));
double lii = min (abs (y[cur] - yi), abs (y[cur] - yii));
r[tot] = min (li, lii);
for (int i = 1; i <= tot - 1; i++)
{
int id = temp[i];
double ri = sqrt (1.0 * l (cur, id)) - r[i];
if (ri > 0)
r[tot] = min (r[tot], ri);
else
{
r[tot] = 0;
}
}
if (tot == n)
{
double ansi = 0;
for (int i = 1; i <= n; i++)
{
ansi += pi * r[i] * r[i];
}
ansi = abs ((xii - xi) * (yii - yi)) - ansi;
ans = min (ansi, ans);
return;
}
for (int i = 1; i <= n; i++)
{
if (!vis[i])
{
vis[i] = 1;
dfs (tot + 1, i);
vis[i] = 0;
}
}
return;
}
int main ()
{
scanf ("%d%d%d%d%d", &n, &xi, &yi, &xii, &yii);
for (int i = 1; i <= n; i++)
{
scanf ("%d %d", &x[i], &y[i]);
}
for (int i = 1; i <= n; i++)
{
vis[i] = 1;
dfs (1, i);
vis[i] = 0;
}
printf ("%d", int (ans + 0.5));
return 0;
}