luogu题目选做(一)
P1747
DFS
用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;
}
BFS
同样的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;
}
P1746
20分
首先我们可以像上一题一样进行一个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;
}
100分
我们考虑用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;
}
P2298
根据上一题的经验,这道题就不尝试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;
}
P1332
BFS
和前面的题大同小异,不过是在一开始时把多个感染源都加入队列而已
#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;
}
暴力做法
从一个点走到另一个点的最短距离就是曼哈顿距离,所以我暴力的枚举感染源和领主然后根据公式计算曼哈顿距离,并取最小值
#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;
}
P1683
题目看似是让找距离实际上是让找有多少点与起点联通,所以我们只需要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;
}
P1106
从高位开始一旦出现大数位于小数的前面,删除大数即可。然后要处理前导零。
没有必要将字符串转发成数字,可以直接用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;
}
P4447
排序,队列,暴力
首先将原数列排序。然后逐个遍历。
对于第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;
}
P1021
用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;
}
P1657
因为数据范围非常小,我们可以暴力的用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;
}