Description

JZOJ.5307【NOIP2017模拟8.18】偷窃
 

Input

JZOJ.5307【NOIP2017模拟8.18】偷窃

Output

JZOJ.5307【NOIP2017模拟8.18】偷窃
 

Sample Input

5 5
1 4 0 5 2
2 1 2 0 1
0 2 3 4 4
0 3 0 3 1
1 2 2 1 1

Sample Output

9
 

Data Constraint

JZOJ.5307【NOIP2017模拟8.18】偷窃
 

Hint

JZOJ.5307【NOIP2017模拟8.18】偷窃

本题直接做比较麻烦,加上金砖可移动。

这题就是要求我们找出一种摆放方式,三视图与之前一样且用得金块数最少。

我们可以重新构造。

对于俯视图,它提供的信息就是我们要在哪里放金砖,哪里不能放金砖。

对于正视图和侧视图,它提供的信息就是某一行或某一列的最大高度有多高。

那么我们就根据这三视图提供的信息一一构造。

对于俯视图,我们就将一开始为0的地方就视为不可放东西。

要求放置的金砖数最少,自然我们想某一列的最大高度与某一行的最大高度一样,这样我们就用那么高的金砖就能满足正视图和侧视图的某一行列的高度。

很显然这是最好的方案。

于是我们统计每一行的最大高度和每一列的最大高度,看看能不能把某一行和某一列的同样的高度用一个高度的金砖”柱子“来代替。

这就是二分图匹配啦~

行在左,列在右,相等高度一连线,跑个匈牙利算法,能匹配的高度一次算,未能匹配后加上,再把剩下的非零的格子都视为1,统计下金砖数和一开始的总金砖数做个差就是答案了。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #define N 105
 5 using namespace std;
 6 int f[N][N],maxl[N],maxh[N],f1[N],f2[N],used[N];
 7 long long ans,sum,n,m,zero,ji;
 8 bool find(int x){
 9     for (int i=1;i<=m;i++)
10      if ((maxl[i]==maxh[x])&&(used[i]==false))
11       if ((maxl[i]==0)||(f[x][i]!=0)){
12          used[i]=true;
13          if ((f1[i]==0)||find(f1[i])){
14              f1[i]=x;
15              f2[x]=i;
16              return true;
17          }
18      }
19      return false;
20 }
21 int main(){
22     scanf("%lld%lld",&n,&m);
23     for (int i=1;i<=n;i++)
24      for (int j=1;j<=m;j++){
25          scanf("%d",&f[i][j]);
26          sum+=f[i][j];
27          if (f[i][j]==0) zero++;
28          maxl[j]=max(maxl[j],f[i][j]);
29          maxh[i]=max(maxh[i],f[i][j]);
30      }
31     ans=0;
32     for (int i=1;i<=n;i++){
33         memset(used,false,sizeof(used));
34         if (find(i)) {ans+=maxh[i];
35         if (f[i][f2[i]]!=0) ji++;
36         }
37     }
38     for (int i=1;i<=n;i++)
39         if ((f2[i]==0)&&(maxh[i]!=0)) ans+=maxh[i],ji++;
40     for (int i=1;i<=m;i++)
41         if ((f1[i]==0)&&(maxl[i]!=0)) ans+=maxl[i],ji++;
42     printf("%lld\n",sum-ans-(n*m-zero-ji));
43     return 0;
44 }
神奇的代码

相关文章: