春節假期長時間看手機有完美姿勢嗎?衛健委官方科普
阿新 • • 發佈:2021-02-14
文章目錄
R e s u l t Result Result
H y p e r l i n k Hyperlink Hyperlink
https://www.luogu.com.cn/problem/P3231
D
e
s
c
r
i
p
t
i
o
n
Description
Description
一個 a × b × c a\times b\times c a×b×c的三維立方體內有若干個點,你每次可以選擇一個大小為 x × y × z x\times y\times z x×y×z的矩陣進行清除,可以將這個矩陣內所有的點刪除,花費 m i n { x , y , z } min\{x,y,z\} min{x,y,z}的代價,問刪除所有點的最少代價
資料範圍:
a
×
b
×
c
≤
5
×
1
0
3
a\times b\times c\leq 5\times 10^3
a×b×c≤5×103
S o l u t i o n Solution Solution
首先,我們肯定是每次刪除一個面,這樣肯定是最優的(仔細想想為什麼)
又因為
3
5000
<
18
_3\sqrt{5000}< 18
35000
<18,說明
a
,
b
,
c
a,b,c
a,b,c當中至少有一個小於18,
自然我們令
a
a
a是最小的,只需要
2
a
2^a
2a列舉每個面是否直接刪掉,如果不刪掉,則把這些面壓扁轉換成平面的情況
轉換成平面的情況後,問題就變成了 b × c b\times c b×c的平面上有若干個點,每次可以選擇刪除一行/列,問最少刪除多少次
顯然是個最大匹配問題,用匈牙利演算法即可
時間複雜度: O ( 2 a × b 2 × c 2 ) O(2^a\times b^2\times c^2) O(2a×b2×c2)
C o d e Code Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 5010
#define M 5010
#define LL long long
using namespace std;
struct node{int next,to;}edge[M];bool vis[N];int tot,l[N],link[N];
void add(int u,int v){edge[++tot].to=v;edge[tot].next=l[u];l[u]=tot;return;}
inline LL read()
{
char c;LL f=0,d=1;
while((c=getchar())<48||c>57)if(c=='-')d=-1;f=(f<<3)+(f<<1)+c-48;
while((c=getchar())>=48&&c<=57)f=(f<<3)+(f<<1)+c-48;
return d*f;
}
inline int find(int p)
{
for(int i=l[p];i;i=edge[i].next)
if(!vis[edge[i].to])
{
vis[edge[i].to]=1;
int q=link[edge[i].to];
link[edge[i].to]=p;
if(!q||find(q)) return true;
link[edge[i].to]=q;
}
return false;
}
int T,a,b,c,minn,p[3][N],cnt,x,ans;
bool flg[N];
inline void work(int x)
{
int now=0,tmp=0;
for(register int i=1;i<=a;i++) flg[i]=(x>>(i-1))&1,tmp+=(x>>(i-1))&1;
fill(l+1,l+1+b,0);tot=0;fill(link+1,link+1+c,0);
for(register int i=1;i<=cnt;i++) if(!flg[p[0][i]]) add(p[1][i],p[2][i]);
for(register int i=1;i<=b;i++)
{
fill(vis+1,vis+1+c,0);
tmp+=find(i);
}
ans=min(ans,tmp);return;
}
int main()
{
T=read();
while(T--)
{
a=read();b=read();c=read();ans=0x7fffffff;cnt=0;
minn=min(a,min(b,c));
for(register int i=1;i<=a;i++)
for(register int j=1;j<=b;j++)
for(register int k=1;k<=c;k++)
{
x=read();
if(x) p[0][++cnt]=i,p[1][cnt]=j,p[2][cnt]=k;
}
if(minn==b) swap(a,b),swap(p[0],p[1]);else if(minn==c) swap(a,c),swap(p[0],p[2]);
for(register int i=0;i<(1<<a);i++) work(i);
printf("%d\n",ans);
}
}