luogu题目选做(一)

1|0P1747


1|1DFS


f[x][y]储存从(1,1)(x,y)的最短距离

每搜索到一个点时首先判断该点时候在棋盘中。在判断当前步骤时候是当前最优解。如果是最优解就用该店去更新其他点。

#include <bits/stdc++.h> using namespace std; const int N = 30 , INF = 0x7f7f7f7f; const int dx[12] = { 1 , 1 , -1 , -1 , 2 , 2 , -2 , -2 , 2 , 2 , -2 , -2 }; const int dy[12] = { 2 , -2 , 2 , -2 , 1 , -1 , 1 , -1 , 2 , -2 , 2 , -2 }; int a , b , c , d , f[N][N]; void dfs( int x , int y , int step ) { if( x < 1 || y < 1 || y > 20 || x > 20 ) return ; if( step >= f[x][y] ) return ; f[x][y] = step; for( int i = 0 ; i < 12 ; i ++ ) dfs( x + dx[i] , y + dy[i] , step + 1 ); return ; } int main() { cin >> a >> b >> c >> d; memset( f , INF , sizeof( f ) ); dfs( 1 , 1 , 0 ); cout << f[a][b] << endl << f[c][d] << endl; return 0; }

1|2BFS


同样的f[x][y]

因为BFS的特点所以第一次到达的点一定是最有解,再次访问时一定不是最优解。

如果该点是第一次访问,更新答案的同时,再去扩展该点所能到达的点。

#include <bits/stdc++.h> using namespace std; const int N = 30 , INF = 0x7f7f7f7f; const int dx[12] = { 1 , 1 , -1 , -1 , 2 , 2 , -2 , -2 , 2 , 2 , -2 , -2 }; const int dy[12] = { 2 , -2 , 2 , -2 , 1 , -1 , 1 , -1 , 2 , -2 , 2 , -2 }; int a , b , c , d , f[N][N]; struct node { int x , y , step; } cur; queue< node > q; inline node make_node ( int x , int y , int step ) { cur.x = x , cur.y = y , cur.step = step; return cur; } void bfs() { q.push( make_node( 1 , 1 , 0 ) ); int x , y , step; while( !q.empty() ) { x = q.front().x , y = q.front().y , step = q.front().step , q.pop(); if( x < 1 || x > 20 | y < 1 || y > 20 ) continue; if( f[x][y] ) continue; f[x][y] = step; for( int i = 0 ; i < 12 ; i ++ ) q.push( make_node( x + dx[i] , y + dy[i] , step + 1 ) ); } return ; } int main() { cin >> a >> b >> c >> d; bfs(); cout << f[a][b] << endl << f[c][d] << endl; return 0; }

2|0P1746


2|120分


首先我们可以像上一题一样进行一个DFS,然后常规的三大剪枝,记忆化、可行性、最优性。然后就T了,只能拿到20分

#include <bits/stdc++.h> using namespace std; const int N = 1005 , INF = 0x7f7f7f7f; const int dx[4] = { 1 , -1 , 0 , 0 } , dy[4] = { 0 , 0 , -1 , 1 }; int n , x_1 , x_2 , y_1 , y_2 , f[N][N]; bool v[N][N] = {}; void dfs( int x , int y , int step ) { if( x < 1 || x > n || y < 1 || y > n ) return ; if( v[x][y] ) return ; if( step >= f[x][y] ) return ; f[x][y] = step; for( int i = 0 ; i < 4 ; i ++ ) dfs( x + dx[i] , y + dy[i] , step + 1 ); return ; } int main() { cin >> n; for( int i = 1 , x ; i <= n ; i ++ ) { for( int j = 1 ; j <= n ; j ++ ) { x = getchar() - '0'; while( x != 0 && x != 1 ) x = getchar() - '0'; v[i][j] = x; } } cin >> x_1 >> y_1 >> x_2 >> y_2; memset( f , INF , sizeof( f ) ); dfs( x_1 , y_1 , 0 ); cout << f[ x_2 ][ y_2 ] << endl; return 0; }

2|2100分


我们考虑用BFS做,可以大大减少我们重复操作,并且当我们第一次搜索到目标点就可以结束搜索。同时就是想DFS处理一下边界就好。

这道题也告诉我们并不是所有的题都适合用DFS来做,所以我们要训练估计算法复杂度。

#include <bits/stdc++.h> using namespace std; const int N = 1005 , INF = 0x7f7f7f7f; const int dx[4] = { 1 , -1 , 0 , 0 } , dy[4] = { 0 , 0 , -1 , 1 }; int n , x_1 , x_2 , y_1 , y_2 , f[N][N]; bool v[N][N] = {}; struct node { int x , y , step; }cur; queue< node > q; inline node make_node( int x , int y , int step ) { cur.x = x , cur.y = y , cur.step = step; return cur; } void bfs() { q.push( make_node( x_1 , y_1 , 0 ) ); int x , y , step; while( !q.empty() ) { x = q.front().x , y = q.front().y , step = q.front().step , q.pop(); if( x < 1 || x > n || y < 1 || y > n ) continue; if( v[x][y] ) continue; if( f[x][y] <= step ) continue; f[x][y] = step; if( x == x_2 && y == y_2 ) break; for( int i = 0 ; i < 4 ; i ++ ) q.push( make_node( x + dx[i] , y + dy[i] , step + 1 ) ); } } int main() { cin >> n; for( int i = 1 , x ; i <= n ; i ++ ) { for( int j = 1 ; j <= n ; j ++ ) { x = getchar() - '0'; while( x != 0 && x != 1 ) x = getchar() - '0'; v[i][j] = x; } } cin >> x_1 >> y_1 >> x_2 >> y_2; memset( f , INF , sizeof( f ) ); bfs(); cout << f[ x_2 ][ y_2 ] << endl; return 0; }

3|0P2298


根据上一题的经验,这道题就不尝试DFS了,直接用DFS实现,方法和上一题大同小异,注意特判找不到的情况,其他不变。

#include<bits/stdc++.h> using namespace std; const int N = 2005 , dx[4] = { 0 , 0 , 1 , -1 } , dy[4] = { 1 , -1 , 0 , 0 }; int n , m , s , t , ans = -1 ; short f[N][N] ; bool v[N][N]; struct node { int x , y , step; }cur; queue < node > q; inline node make_node( int x , int y , int step ) { cur.x = x , cur.y = y , cur.step = step; return cur; } void bfs() { q.push( make_node( s , t , 0 ) ); int x , y , step; while( !q.empty() ) { x = q.front().x , y = q.front().y , step = q.front().step , q.pop(); if( x < 1 || x > n || y < 1 || y > m ) continue; if( v[x][y] ) continue; v[x][y] = 1; if( f[x][y] == -1 ) continue; if( f[x][y] == 1 ) { ans = step; return ; } for( int i = 0 ; i < 4 ; i ++ ) q.push( make_node( x + dx[i] , y + dy[i] , step + 1 ) ) ; } } int main() { cin >> n >> m; char ch ; for ( int i = 1 ; i <= n ; i ++ ) { for( int j = 1 ; j <= m ; j ++ ) { ch = getchar(); while( ch != 'm' && ch != 'd' && ch != '#' && ch != '.' ) ch = getchar(); if( ch == 'm' ) s = i , t = j; else if( ch == 'd' ) f[i][j] = 1 ; else if( ch == '#' ) f[i][j] = -1 ; } } bfs(); if( ans == -1 ) puts("No Way!"); else cout << ans << endl; return 0; }

4|0P1332


4|1BFS


和前面的题大同小异,不过是在一开始时把多个感染源都加入队列而已

#include <bits/stdc++.h> using namespace std; const int N = 505 , M = 1e5 + 5 , dx[4] = { 0 , 0 , -1 , 1 } , dy[4] = { 1 , -1 , 0 , 0 } , INF = 0x7f7f7f7f; int n , m , a , b , f[N][N] , v[N][N] ; struct node { int x , y , step; } cur; queue < node > q; inline node make_node( int x , int y , int step ) { cur.x = x , cur.y = y , cur.step = step; return cur; } inline int read() { register int x = 0; register char ch = getchar(); while( ch < '0' || ch > '9' ) ch = getchar(); while( ch >= '0' && ch <= '9' ) x = ( x << 3 ) + ( x << 1 ) + ch - '0' , ch = getchar(); return x; } int main() { n = read() , m = read() , a = read() , b = read(); memset( f , INF , sizeof( f ) ); for( int i = 1 , x , y ; i <= a ; i ++ ) x = read() , y = read() , q.push( make_node( x , y , 0 ) ); while( !q.empty() ) { register int x = q.front().x , y = q.front().y , step = q.front().step; q.pop(); if( x < 1 || x > n || y < 1 || y > m ) continue; if( f[x][y] <= step ) continue; f[x][y] = step; for( int i = 0 ; i < 4 ; i ++ ) q.push( make_node( x + dx[i] , y + dy[i] , step + 1 ) ); } for( int i = 1 , x , y ; i <= b ; i ++ ) x = read() , y = read() , cout << f[x][y] << endl; return 0; }

4|2暴力做法


从一个点走到另一个点的最短距离就是曼哈顿距离,所以我暴力的枚举感染源和领主然后根据公式计算曼哈顿距离,并取最小值

#include <bits/stdc++.h> using namespace std; const int N = 1e5+5 , INF = 0x7f7f7f7f; int n, m , s[N][2]; inline int read() { register int x = 0; register char ch = getchar(); while( ch < '0' || ch > '9' ) ch = getchar(); while( ch >= '0' && ch <= '9' ) x = ( x << 3 ) + ( x << 1 ) + ch - '0' , ch = getchar(); return x; } int main() { read() , read() , n = read() , m = read(); for( int i = 1 ; i <= n ; i ++ ) s[i][0] = read() , s[i][1] = read(); for( int i = 1 , x , y , ans ; i <= m ; i ++ ) { x = read() , y = read() , ans = INF; for( int j = 1 ; j <= n ; j ++ ) ans = min( ans , abs( x - s[j][0] ) + abs( y - s[j][1] ) ); cout << ans << endl; } return 0; }

5|0P1683


题目看似是让找距离实际上是让找有多少点与起点联通,所以我们只需要dfs遍历一遍图即可

#include <bits/stdc++.h> using namespace std; const int N = 25 , dx[] = { 0 , 0 , 1 , -1 } , dy[] = { 1 , -1 , 0 , 0 }; int n , m , v[N][N] , f[N][N] , ans = 0 , s , t ; inline int read() { register char ch = getchar(); while( ch != '.' && ch != '#' && ch != '@' ) ch = getchar(); return ( ch == '.' ? 0 : ( ch == '#' ? 1 : 2 ) ); } void dfs( int x , int y ) { if( x < 1 || y < 1 || x > n || y > m ) return ; if( v[x][y] ) return ; v[x][y] = 2 , ans ++ ; for( int i = 0 ; i < 4 ; i ++ ) dfs( x + dx[i] , y + dy[i] ); return ; } int main() { cin >> m >> n; for( int i = 1 ; i <= n ; i ++ ) { for( int j = 1 ; j <= m ; j ++ ) { v[i][j] = read(); if( v[i][j] == 2 ) s = i , t = j , v[i][j] = 0; } } dfs( s , t ); cout << ans << endl; return 0; }

6|0P1106


从高位开始一旦出现大数位于小数的前面,删除大数即可。然后要处理前导零。

没有必要将字符串转发成数字,可以直接用erase函数删除元素,并且最后输出即可。

特判一下全部删完的情况即可。

#include<bits/stdc++.h> using namespace std; int n , cnt = 0 , i = 0; string s; int main() { cin >> s >> n; while( 1 ) { if( s[i] > s[ i + 1 ] ) { s.erase( i , 1 ) , cnt ++ , i = 0; while( s[0] == '0' ) s.erase( 0 , 1 ); if( cnt == n ) break; } else i ++; } if( s.size() == 0 ) puts("0"); else cout << s; return 0; }

7|0P4447


排序,队列,暴力

首先将原数列排序。然后逐个遍历。

对于第i个数a[i],我们遍历所有队列,如果队尾是a[i] -1则将该数插入该队列。

因为要最小值最大,所以队尾相同时优先插入队列短的。

最后遍历所有队列,对队列大小取最小值即为答案。

#include <bits/stdc++.h> using namespace std; const int N = 1e6 + 5; int n , a[N] , cnt = 0 , ans = N; pair< int , int > e[N]; inline int read() { register int x = 0 , f = 1; register char ch = getchar(); while( ( ch < '0' || ch > '9' ) && ch != '-' ) ch = getchar(); if( ch == '-' ) ch = getchar() , f = -1; while( ch >= '0' && ch <= '9' ) x = ( x << 3 ) + ( x << 1 ) + ch - '0' , ch = getchar(); return x * f; } int main() { n = read(); for( int i = 1 ; i <= n ; i ++ ) a[i] = read(); sort( a + 1 , a + 1 + n ); for( int i = 1 , f ; i <= n ; i ++ ) { f = 0; sort( e + 1 , e + 1 + cnt ); for( int j = 1 ; j <= cnt ; j ++ ) { if( a[i] - 1 > e[j].first ) continue; else if( a[i] - 1 < e[j].first ) break; e[j].first = a[i] , e[j].second ++ , f = 1 ;break; } if( f ) continue; e[ ++ cnt ] = { a[i] , 1 }; } for( int i = 1 ; i <= cnt ; i ++ ) ans = min( ans , e[i].second ); cout << ans << endl; return 0; }

8|0P1021


用DFS枚举所有的所有的情况然后利用DP判断。

假如我们已经确定了前i个数,且第i个数为nums[i],前i个数所能组成的最大值为end则第i+1个数的取值范围是[ nums[i] + 1 , end + 1 ]

f[i]为构成数字i所需的邮票张数,则有f[i] = min( f[i] , f[ i - nums[j] ] + 1 )DP后,满足f[i] <= n的i的最大值即为所求。对于end我们也可以用这种方法求出

#include <bits/stdc++.h> using namespace std; const int N = 20 , M = 50000 , INF = 0x7f7f7f7f; int n , k , nums[N] , ans_nums[N] , f[M] , ans = -1; int slove( int x ) { memset( f , INF , sizeof( f ) ) , f[0] = 0; for( int i = 1 ; i <= x ; i ++ ) { for( int j = nums[i] ; j <= nums[i] * n ; j ++ ) f[j] = min( f[j] , f[ j - nums[i] ] + 1 ); } for( int i = 1 ; i <= nums[x] * n ; i ++ ) if( f[i] > n ) return i - 1; } void dfs( int x ) { int end = slove( x - 1 ); if( x - 1 == k ) { if( end > ans ) ans = end , memcpy( ans_nums , nums , sizeof( nums ) ); return ; } for( int i = nums[ x - 1 ] + 1 ; i <= end + 1 ; i ++ ) nums[x] = i , dfs( x + 1 ); } int main() { cin >> n >> k; nums[1] = 1; dfs( 2 ); for( int i = 1 ; i <= k ; i ++ ) cout << ans_nums[i] << ' '; cout << "\nMAX=" << ans << "\n"; return 0; }

9|0P1657


因为数据范围非常小,我们可以暴力的用DFS枚举每个人选择了哪本书,并判断时候有多个人选择了相同的书。

注意特判n==0的情况。

#include <bits/stdc++.h> using namespace std; const int N = 23; int n , a[N][2] , cnt = 0; bool vis[N]; void dfs( int x ) { if( x == n + 1 ) { cnt ++ ; return ; } if( !vis[ a[x][0] ] ) vis[ a[x][0] ] = 1 , dfs( x + 1 ) , vis[ a[x][0] ] = 0; if( !vis[ a[x][1] ] ) vis[ a[x][1] ] = 1 , dfs( x + 1 ) , vis[ a[x][1] ] = 0; } int main() { cin >> n; if( n == 0 ) puts("0") , exit(0); for( int i = 1 ; i <= n ; i ++ ) cin >> a[i][0] >> a[i][1]; dfs( 1 ); cout << cnt << endl; return 0; }

__EOF__

本文作者PHarr
本文链接https://www.cnblogs.com/PHarr/p/15362601.html
关于博主:前OIer,SMUer
版权声明CC BY-NC 4.0
声援博主:如果这篇文章对您有帮助,不妨给我点个赞
posted @   PHarr  阅读(40)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示