JZOJ4994. 棋盘

题目大意

给定坐标系内的 n 个两两不同的点。
现在可以任意将这些点的y坐标顺序打乱,求在保证不出现重点的情况下,与原来的的点相同的点的个数最少是多少。

Data Constraint
n50

题解

考虑费用流。
X Y拆成二分图。
假如原图有一个点 (x,y) 那就 xy 连一条费用为 1 容量为1的边,否则就就 xy 连一条费用为 0 容量为1的边,然后源点和汇点再限制一下数量即可。
最后跑一遍费用流。

SRC

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std ;

#define N 50 + 10
#define M 100000 + 10
struct Point {
    int x , y ;
    Point ( int X = 0 , int Y = 0 ) { x = X , y = Y ; }
} P[N] ;
struct Note {
    int v , h ;
} tpx[N] , tpy[N] ;

bool Map[N][N] , vis[2*N] ;
int Node[4*M] , Next[4*M] , Head[2*M] , C[2*M] , Cost[2*M] , tot = 1 ;
int Numx[N] , Numy[N] , D[M] , Dist[2*N] , Pre[M] ;
int n , ans , Cntx , Cnty , S , T ;

bool cmp( Note a , Note b ) { return a.v < b.v ; }

void PreX() {
    sort( tpx + 1 , tpx + n + 1 , cmp ) ;
    Cntx = 0 ;
    for (int i = 1 ; i <= n ; i ++ ) {
        if ( tpx[i].v != tpx[i-1].v ) ++ Cntx ;
        P[tpx[i].h].x = Cntx ;
        Numx[Cntx] ++ ;
    }
}

void PreY() {
    sort( tpy + 1 , tpy + n + 1 , cmp ) ;
    Cnty = 0 ;
    for (int i = 1 ; i <= n ; i ++ ) {
        if ( tpy[i].v != tpy[i-1].v ) ++ Cnty ;
        P[tpy[i].h].y = Cnty ;
        Numy[Cnty] ++ ;
    }
}

void link( int u , int v , int w , int cost ) {
    Node[++tot] = v , Next[tot] = Head[u] , C[tot] = w , Cost[tot] = cost , Head[u] = tot ;
    Node[++tot] = u , Next[tot] = Head[v] , C[tot] = 0 , Cost[tot] = -cost , Head[v] = tot ;
}

bool SPFA() {
    int i = 0 , j = 1 ;
    memset( Dist , 63 , sizeof(Dist) ) ;
    D[1] = S ;
    Dist[S] = 0 ;
    vis[S] = 1 ;
    while ( i < j ) {
        i ++ ;
        int now = D[i] ;
        for (int p = Head[now] ; p ; p = Next[p] ) {
            if ( !C[p] ) continue ;
            if ( Dist[now] + Cost[p] < Dist[Node[p]] ) {
                Dist[Node[p]] = Dist[now] + Cost[p] ;
                Pre[Node[p]] = p ;
                if ( !vis[Node[p]] ) {
                    D[++j] = Node[p] ;
                    vis[Node[p]] = 1 ;
                }
            }
        }
        vis[now] = 0 ;
    }
    return Dist[T] < Dist[T+1] ;
}

int main() {
    freopen( "chess.in" , "r" , stdin ) ;
    freopen( "chess.out" , "w" , stdout ) ;
    scanf( "%d" , &n ) ;
    for (int i = 1 ; i <= n ; i ++ ) {
        scanf( "%d%d" , &P[i].x , &P[i].y ) ;
        tpx[i].v = P[i].x , tpx[i].h = i ;
        tpy[i].v = P[i].y , tpy[i].h = i ;
    }
    PreX() , PreY() ;
    S = 0 , T = Cntx + Cnty + 1 ;
    for (int i = 1 ; i <= n ; i ++ ) Map[P[i].x][P[i].y] = 1 ;
    for (int i = 1 ; i <= Cntx ; i ++ ) {
        for (int j = 1 ; j <= Cnty ; j ++ ) {
            link( i , Cntx + j , 1 , Map[i][j] ) ;
        }
    }
    for (int i = 1 ; i <= Cntx ; i ++ ) link( S , i , Numx[i] , 0 ) ;
    for (int i = 1 ; i <= Cnty ; i ++ ) link( Cntx + i , T , Numy[i] , 0 ) ;
    while ( SPFA() ) {
        int Minv = 0x7FFFFFFF ;
        for (int x = T ; x != S ; x = Node[Pre[x]^1] ) Minv = min( Minv , C[Pre[x]] ) ;
        ans += Minv * Dist[T] ;
        for (int x = T ; x != S ; x = Node[Pre[x]^1] ) {
            C[Pre[x]] -= Minv ;
            C[Pre[x]^1] += Minv ;
        }
    }
    printf( "%d\n" , ans ) ;
    return 0 ;
}

以上.

好的,这是一个经典的问题。国际象棋棋盘上的骑士可以沿着L形移动,即先向上(或下)走两步,再向左(或右)走一步;或者先向左(或右)走两步,再向上(或下)走一步。为了确定处于骑士攻击下的格子个数,我们可以按照以下步骤进行: 1. 枚举棋盘上的每一个格子,判断该格子是否处于骑士攻击下。 2. 对于每一个格子,判断它的八个方向是否存在骑士攻击,如果存在,则将该格子计入答案。 具体实现可以使用两层循环来枚举棋盘上的每一个格子,再使用一些判断语句来判断该格子周围的八个方向是否存在骑士攻击。具体代码实现如下(假设棋盘大小为8x8): ``` int cnt = 0; // 计数器,记录处于骑士攻击下的格子个数 for(int i = 0; i < 8; i++) { for(int j = 0; j < 8; j++) { // 判断该格子是否处于骑士攻击下 if((i >= 2 && j >= 1 && board[i-2][j-1] == 'K') || (i >= 2 && j <= 6 && board[i-2][j+1] == 'K') || (i >= 1 && j >= 2 && board[i-1][j-2] == 'K') || (i >= 1 && j <= 5 && board[i-1][j+2] == 'K') || (i <= 6 && j >= 2 && board[i+1][j-2] == 'K') || (i <= 6 && j <= 5 && board[i+1][j+2] == 'K') || (i <= 5 && j >= 1 && board[i+2][j-1] == 'K') || (i <= 5 && j <= 6 && board[i+2][j+1] == 'K')) { cnt++; } } } // 输出结果 cout << cnt << endl; ``` 其中,board是一个字符型的二维数组,表示棋盘上每个格子的状态('K'表示该格子上有一个骑士,'.'表示该格子为空)。代码中的注释已经解释了每个判断语句的含义。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值