[bzoj 4950--Wf2017]Mission Improbable

124 篇文章 2 订阅
8 篇文章 0 订阅

那是春日里一个天气晴朗的好日子,你准备去见见你的老朋友Patrick,也是你之前的犯罪同伙。Patrick在编程竞赛上豪赌输掉了一大笔钱,所以他需要再干一票。为此他需要你的帮助,虽然你已经金盆洗手了。你刚开始很不情愿,因为你一点也不想再回到那条老路上了,但是你觉得听一下他的计划也无伤大雅。在附近的一个仓库里有一批货物,包含一些贵重的消费性部件,Patrick企图从中尽可能多地偷些东西出来。这意味着要找一条进去的路,弄晕安保人员,穿过各种各样的激光射线,你懂的,都是常见的抢劫技术。然而,仓库的核心装备了一套Patrick搞不定的安保系统。这也是他需要你帮助他的地方。这批货物被放置在一些巨大的立方体箱里,每个箱子的尺寸都是相同的。这些箱子堆放成许多整齐的堆,每个箱子可以表示成一个三维的网格。安保系统每个小时会用三台相机对这堆货物进行一次拍照,相机分别为:前置相机(front camera),侧置相机(side camera)和顶置相机(top camera)。前置相机的照 片显示了每一行最高的那堆箱子的高度,侧置相机显示了每一列最高的那堆箱子的高度,顶置相机显示了每个位置是否存在一堆箱子。如果安保系统发现任何一张照片出现了变化,它会立即拉响警报。一旦 Patrick 进去了,他会确定每堆箱子的高度并且发给你。
Patrick想尽可能多偷走一些箱子。由于他不能弄坏安保系统,他准备重新安排剩余每堆箱子的放置,使得下一次相机取像时会得到相同的照片,从而骗过安保系统。在上面的例子中,他可以偷走九个箱子。图2显示了一种可能的剩余箱子的安置方案能使得安保系统认为与原安置情况相同。Patrick想请你帮他确定在保证能骗过安保系统的情况下他最多能偷走多少个箱子。你会帮他干完这最后一票么?

好题,也可能是我太菜了,自我感觉挺巧妙的。首先我们一开始先把所有不为0的高度去到1,加上贡献,但是我们要使得得到相同的照片,所以要把每行每列的最大值给还回去。
这样就结束了嘛,不对,看样例就知道了,因为有可能第i行的最大值与第j列的最大值相同,那么我们只要第i行第j列变成最大值,便可以少用一个最大值,不用两个。而一行只能和一列匹配,所以就想到了二分图匹配,这题就解决了。
这道题给我的启发是不用想太复杂,要简化模型,考虑真正有用的贡献即可。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
using namespace std;
struct node
{
    int x,y,next;
}a[110000];int len,last[110];
void ins(int x,int y)
{
    len++;
    a[len].x=x;a[len].y=y;
    a[len].next=last[x];last[x]=len;
}
long long s[110][110],mh[110],ml[110];
int match[110],chw[110];
bool find_muniu(int x,int t)
{
    for(int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        if(chw[y]!=t)
        {
            chw[y]=t;
            if(match[y]==0 || find_muniu(match[y],t)==true)
            {
                match[y]=x;
                return true;
            }
        }
    }
    return false;
}
int main()
{
    long long ans=0;
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            scanf("%lld",&s[i][j]);
            mh[i]=max(mh[i],s[i][j]);
            ml[j]=max(ml[j],s[i][j]);
            if(s[i][j])ans+=s[i][j]-1;
        }
    }
    for(int i=1;i<=n;i++)if(mh[i])ans-=(mh[i]-1);
    for(int i=1;i<=m;i++)if(ml[i])ans-=(ml[i]-1);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            if(mh[i]==ml[j] && s[i][j])ins(i,j);
        }
    }
    for(int i=1;i<=n;i++)
    {
        if(find_muniu(i,i)==true)ans+=mh[i]-1;
    }
    printf("%lld\n",ans);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值