任务说明:这里的搜索不仅包含了dfs和bfs,还包括剪枝、记录等技巧以加快速度。
[USACO06FEB]数字三角形Backward Digit Su…
滑雪
吃奶酪
靶形数独
P1118 [USACO06FEB]数字三角形Backward Digit Su…
有这么一个游戏:写出一个1~N的排列a[i],然后每次将相邻两个数相加,构成新的序列,再对新序列进行这样的操作,显然每次构成的序列都比上一次的序列长度少1,直到只剩下一个数字位置。
最后得到16这样一个数字。
现在想要倒着玩这样一个游戏,如果知道N,知道最后得到的数字的大小sum,请你求出最初序列a[i],为1~N的一个排列。若答案有多种可能,则输出排序最小的那一个。
解答:第一次我想直接next_permutation,然后在嵌套循环计算顶层数字,结果TLE了好几个点。
后来看了题解,大家都说的杨辉三角,我还是没懂。专门搜索了题解才看懂。
最后的结果其实是一个杨辉三角。(拿样例来说,第4层的杨辉三角是1 3 3 1; 1∗3+3∗1+3∗2+1∗4=16)
然后知道杨辉三角怎么用了之后,直接dfs求全排列,如果超过了sum值就回溯。
1 #include <bits/stdc++.h> 2 #include <string> 3 using namespace std; 4 5 int n, sum; 6 int yanghuisanjiao[20][20] = {0}; 7 int used[20] = {0}; 8 9 void print(vector<int>& vec, std::string& strDes) { 10 printf("%s: ", strDes.c_str()); 11 for (int i = 0; i < vec.size(); ++i) { 12 printf("%d ", vec[i]); 13 } 14 printf(" "); 15 } 16 17 void print(vector<int>& vec) { 18 for (int i = 0; i < vec.size(); ++i) { 19 printf("%d ", vec[i]); 20 } 21 printf(" "); 22 } 23 24 bool dfs(int level, vector<int>& permutation, int cal_sum, int idx) { 25 if (level == n + 1) { 26 if (cal_sum == sum) { 27 print(permutation); 28 return true; 29 } 30 return false; 31 } 32 for (int i = 0; i < n; ++i) { 33 if (!used[i+1]) { 34 permutation[idx] = i + 1; 35 used[i+1] = 1; 36 //如果求和大于sum,就剪枝 37 if (cal_sum + yanghuisanjiao[n-1][idx] * permutation[idx] > sum) { 38 used[i+1] = false; 39 return false; 40 } 41 bool ans = dfs(level + 1, permutation, cal_sum + yanghuisanjiao[n-1][idx] * permutation[idx], idx + 1); 42 if (ans == true) { 43 return true; 44 } 45 used[i+1] = 0; 46 } 47 } 48 //找到最后也没找到, 就return false 49 return false; 50 } 51 52 53 int main() { 54 cin >> n >> sum; 55 56 //[1]计算杨辉三角 57 for (int i = 0; i < n ; ++i) { 58 for (int j = 0; j <= i; ++j) { 59 if (j == 0 || j == i ) { 60 yanghuisanjiao[i][j] = 1; 61 } 62 else { 63 yanghuisanjiao[i][j] = yanghuisanjiao[i-1][j-1] + yanghuisanjiao[i-1][j]; 64 } 65 } 66 } 67 68 //[2]深搜求全排列 69 vector<int> permutation(n, 0); 70 dfs(1, permutation, 0, 0); 71 return 0; 72 }
P1433 吃奶酪
房间里放着n块奶酪。一只小老鼠要把它们都吃掉,问至少要跑多少距离?老鼠一开始在(0,0)点处。
输入:第一行一个数n (n<=15),接下来每行2个实数,表示第i块奶酪的坐标。两点之间的距离公式=sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2))
输出:一个数,表示要跑的最少距离,保留2位小数。
题解:显然贪心不行。那么只能搜索每个点的顺序,求最小值的距离。
1 //这个题目最后也是90分,第五个点TLE,没想明白为啥 2 #include<bits/stdc++.h> 3 #include<unistd.h> 4 #include <sys/time.h> 5 using namespace std; 6 7 typedef pair<double, double> P; 8 int n; 9 10 double ans = DBL_MAX; 11 int addx[4] = {0, -1, 0, 1}; 12 int addy[4] = {-1, 0, 1, 0}; 13 14 void print(vector<vector<double>> vec) { 15 printf("===============debug============ "); 16 for (int i = 0; i < vec.size(); ++i) { 17 for(int j = 0; j < vec.size(); ++j) { 18 printf("%.2f ", vec[i][j]); 19 } 20 printf(" "); 21 } 22 printf("===============end============== "); 23 } 24 25 void dfs(const vector<P>& coor, const double dis[16][16], vector<int>& used, int level, double tmpans, int pre) { 26 if (tmpans >= ans) { 27 return; 28 } 29 if (level == coor.size()) { 30 if (ans > tmpans) { ans = tmpans;} 31 return; 32 } 33 for(int i = 1; i < coor.size(); ++i) { 34 double temp = dis[pre][i] + tmpans; 35 if (!used[i] && temp < ans) { 36 used[i] = 1; 37 dfs(coor, dis, used, level+1, temp, i); 38 used[i] = 0; 39 } 40 } 41 } 42 43 int main() { 44 struct timeval tv; 45 gettimeofday(&tv, NULL); 46 printf("%ld ", tv.tv_sec); 47 scanf("%d", &n); 48 vector<P> coor(n + 1); 49 coor[0] = make_pair(0.0, 0.0); 50 for(int i = 1; i <= n; ++i){ 51 double x, y; 52 scanf("%lf %lf", &x, &y); 53 coor[i] = make_pair(x, y); 54 } 55 56 double dis[16][16] = {0.0}; 57 for(int i = 0; i <= n; ++i) { 58 for(int j = i; j <= n; ++j) { 59 dis[i][j] = dis[j][i] 60 = sqrt((coor[i].first - coor[j].first)*(coor[i].first - coor[j].first) 61 + (coor[i].second - coor[j].second)*(coor[i].second - coor[j].second)); 62 } 63 } 64 65 vector<int> use(n+1); 66 use[0] = 1; 67 dfs(coor, dis, use, 1, 0.0, 0); 68 printf("%.2f ", ans); 69 gettimeofday(&tv, NULL); 70 printf("%ld ", tv.tv_sec); 71 return 0; 72 }