[bzoj-3175][Tjoi2013]攻击装置 题解

题目传送门
题意解析:题目就是给了你一张n*n的图,在图上有的点可以放棋子,有的不可以,这里的棋子跟马一样走“日”,然后问你最多能放几个互不攻击的马。


My opinion:看到这种不能互相攻击的题,暴力点可以用dfs,但是这里n<=200,所以当然是用dfs二分图匹配了,建图十分明显,只要按与一个点能攻击到的所有点连一条边,然后跑二分图匹配就行了。最后的匹配数就是不可以放棋子的点数。但是这里有一个问题,因为你并不能(也许有dalao做到了,但是我这种蒟蒻不行)把点分成两边,所以最后的匹配点对会是正确的两倍。
总结:
1、输入后建图。
2、跑二分图匹配。
3、输出总的0的点数减去ans/2(ans是二分图跑出来的答案)。


跑得贼慢的代码:

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define rep(i,a,n) for (int i=a;i<=n;i++)
#define per(i,a,n) for (int i=a;i>=n;i--)
#define Clear(a,x) memset(a,x,sizeof(a))
#define ll long long
#define INF 2000000000
#define eps 1e-8
using namespace std;
ll read(){
    ll x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9') f=ch=='-'?-1:f,ch=getchar();
    while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
const int maxn=205,maxm=40005;;
const int dx[8]={-1,-2,-2,-1,1,2,2,1},
          dy[8]={-2,-1,1,2,2,1,-1,-2};
char map[maxn][maxn];
int match[maxm];
int head[maxm],vet[maxm<<4],Next[maxm<<4];
bool flag[maxm];
int len,n,sum;
int calc(int x,int y){
    return (x-1)*n+y;
}
void add(int u,int v){
    vet[++len]=v;
    Next[len]=head[u];
    head[u]=len;
}
bool dfs(int u){
    for (int e=head[u];e;e=Next[e]){
        int v=vet[e];
        if (flag[v]) continue;
        int t=match[v];
        match[v]=u;
        flag[v]=1;
        if (t==-1||dfs(t))
            return true;
        match[v]=t;
    }
    return false;
}
int main(){
    n=read();
    rep(i,1,n)
        scanf("%s",map[i]+1);
    rep(i,1,n)
        rep(j,1,n)
            if (map[i][j]=='0')
                rep(k,0,7){
                    int x=i+dx[k],y=j+dy[k];
                    if (x<1||x>n||y<1||y>n||map[x][y]=='1') continue;
                    add(calc(i,j),calc(x,y));
                }
    Clear(match,-1);
    int ans=0;
    rep(i,1,n)
        rep(j,1,n)
        if (map[i][j]=='0'){
            sum++;
            Clear(flag,0);
            flag[calc(i,j)]=1; 
            ans+=dfs(calc(i,j));
        }
    printf("%d\n",sum-ans/2);
    return 0;
}

附上AC记录:
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值