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;
}

posted @ 2021-10-02 20:19  PHarr  阅读(38)  评论(0编辑  收藏  举报