[jzoj5307]【NOIP2017提高A组模拟8.18】偷窃

凡人不可见

不得不说,这题目真的赞(๑•̀ㅂ•́)و✧

Solution

容易想到,题意是求 在保证每行每列的最大值仍旧存在的情况下,所需要的最小砖块数。

考虑贪心,对于一个贡献更大的量的量,优先取

此处指 实际贡献 也就是说 如果该行已经被另一个取了,那么它的贡献只剩下列

所以策略在于优先选行列最大值相等的点

~~其实博主太弱不会证明 ~~╯︿╰

另外一种做法是模型转换 然后二分图匹配

截个图吧反正我看不懂绝不是不负责任
这里写图片描述
这里写图片描述

Code

二分图匹配 (来自网络 侵删)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=150;
int input(){
    int f=1,s=0;char c=getchar();
    while(c<'0' || c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0' && c<='9'){s=s*10+c-'0';c=getchar();}
    return s*f;
}
int n,m;
int side[N],fron[N];
int Link[N],mp[N][N];
bool vis[N],a[N][N];
ll sum,red;
bool find(int x){
    for(int i=1;i<=m;++i)
        if(!vis[i] && a[x][i]){
            vis[i]=1;if(!Link[i] || find(Link[i])){Link[i]=x;return 1;}
        }
    return 0;
}
int main(){
    n=input();m=input();
    for(int i=1;i<=n;++i){
        for(int j=1;j<=m;++j){
            mp[i][j]=input();if(!mp[i][j])continue;
            side[i]=max(side[i],mp[i][j]);
            fron[j]=max(fron[j],mp[i][j]);
            ++red;sum+=mp[i][j];
        }
    }
    for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)if(mp[i][j] && side[i]==fron[j])a[i][j]=1;
    for(int i=1;i<=n;++i){memset(vis,0,sizeof(vis));find(i);}
    for(int i=1;i<=n;++i)if(side[i])red+=side[i]-1;
    for(int i=1;i<=m;++i)if(fron[i] && !Link[i])red+=fron[i]-1;
    printf("%lld\n",sum-red);
    return 0;
}

贪心伪装正解

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define oo 2139062143
#define fo(i,x,y) for (register ll i = (x);i <= (y);++ i)
#define fd(i,x,y) for (register ll i = (x);i >= (y);-- i)
using namespace std;
typedef double db;
typedef long long ll;
ll abs(ll x) {return(((x)>=0)?(x):(-(x)));}
ll max(ll x,ll y) {return(((x)>(y))?(x):(y));}
ll min(ll x,ll y) {return(((x)<(y))?(x):(y));}
ll lowbit(ll x) {return((x)&(-x));}
const ll N = 110;
ll a[N][N];
ll mxx[N],mxy[N];
ll bz[N][N];
ll okx[N],oky[N],b[N];
ll n,m,sum,cost;
int main()
{
    scanf("%lld%lld", &n, &m);
    fo(i,1,n) fo(j,1,m)
    {
        scanf("%lld", &a[i][j]);
        if (a[i][j]) sum += a[i][j];
        mxx[i] = max(a[i][j],mxx[i]);
        mxy[j] = max(a[i][j],mxy[j]);
    }
    fo(i,1,n)
    {
        cost += mxx[i];
        bool ok = 0;
        fo(j,1,m)
        if (!b[j] && a[i][j] && mxy[j] == mxx[i])
        {
           bz[i][j] = mxx[i],ok = b[j] = 1;
           break;
        }
        if (!ok)
            fo(j,1,m)
                if (a[i][j] && mxy[j] >= mxx[i])
                {
                    bz[i][j] = mxx[i];
                    break;
                }
    }
    fo(j,1,m)
    if (!b[j])
    {
        cost += mxy[j];
        fo(i,1,n)
        {
            if (a[i][j] && !bz[i][j] && mxy[j] <= mxx[i])
            {
                bz[i][j] = mxy[j];
                break;
            }
        }
    }
    fo(i,1,n) fo(j,1,m)
    if (a[i][j] && !bz[i][j]) ++ cost;
    printf("%lld", sum - cost);
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值