题目
思路
哪里买的这种
F
\sout{\;F\;}
F试剂,请务必给我来一打。
如果
min
(
x
,
y
,
z
)
=
x
\min(x,y,z)=x
min(x,y,z)=x 的话,干脆把
y
,
z
y,z
y,z 设置为
+
∞
+\infty
+∞ 好了!那么,只要
x
x
x 被包含在了范围内,一切污垢都将被除尽!(正道的光 ♪,照在了大地上……)
而此时,完全可以令 x = 1 x=1 x=1 但操作很多次。毕竟操作次数不重要,重要的是 F F F 试剂的使用量。
所以说,一个需要被消毒的点 ( x 0 , y 0 , z 0 ) (x_0,y_0,z_0) (x0,y0,z0) 只有三种选择:消灭所有 x = x 0 x=x_0 x=x0 的点;消灭所有 y = y 0 y=y_0 y=y0 的点;消灭所有 z = z 0 z=z_0 z=z0 的点。
从简单情况开始想:二维情况。此时, x , y x,y x,y 至少要选一个,即 x x x 不选则 y y y 需要选。这就是 2-sat \text{2-sat} 2-sat 嘛。而且是超级简化版:实际上是二分图,求最大独立集(为可选的 0 0 0 的数量),所以 1 1 1 的数量就是最大匹配的值。
第三维呢?考虑到 1 8 3 = 5832 18^3=5832 183=5832,可以知道 min ( a , b , c ) ⩽ 17 \min(a,b,c)\leqslant 17 min(a,b,c)⩽17,可以暴力枚举。
用 d i n i c \tt dinic dinic 跑最大匹配就好了。但不知道为什么,不开 O 2 O2 O2 竟然会全部 TLE \text{TLE} TLE,没道理啊!
代码
#include <cstdio> // ashamed of Queen of Rock Band
#include <iostream> // I'M ILL, INSANE and DESPERATE
#include <algorithm> // inside of Sister, only sister
#include <cstring> // inside of Principal, only principal
#include <cctype> // inside of me, what do you see
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
typedef long long llong;
inline int readint(){
int a = 0, c = getchar(), f = 1;
for(; !isdigit(c); c=getchar())
if(c == '-') f = -f;
for(; isdigit(c); c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
void writeint(unsigned x){
if(x > 9) writeint(x/10);
putchar(char((x%10)^48));
}
const int MAXN = 10005;
struct Edge{
int to, nxt, val;
Edge() = default;
Edge(int _t,int _n,int _v):to(_t),nxt(_n),val(_v){}
};
Edge e[MAXN<<1];
int head[MAXN], cntEdge;
void addEdge(int a,int b,int c){
e[cntEdge] = Edge(b,head[a],c);
head[a] = cntEdge ++;
e[cntEdge] = Edge(a,head[b],0);
head[b] = cntEdge ++;
}
int que[MAXN], dis[MAXN];
bool bfs(const int S,const int T){
memset(dis+1,-1,T<<2);
int *fro = que, *bac = que;
*(bac ++) = S; dis[S] = 0;
for(; fro!=bac; ++fro){
for(int i=head[*fro]; ~i; i=e[i].nxt)
if(!(~dis[e[i].to]) && e[i].val){
dis[e[i].to] = dis[*fro]+1;
*(bac ++) = e[i].to;
}
}
return (~dis[T]);
}
int cur[MAXN];
int dfs(int x,int inFlow,const int T){
int sum = 0; if(x == T) return inFlow;
for(int &i=cur[x]; ~i; i=e[i].nxt)
if(dis[e[i].to] == dis[x]+1 && e[i].val){
int d = dfs(e[i].to,min(inFlow-sum,e[i].val),T);
e[i].val -= d, e[i^1].val += d;
if((sum += d) == inFlow) break;
}
if(sum != inFlow) dis[x] = -1;
return sum;
}
const int INF = 0x3fffffff;
int dinic(const int S,const int T){
int res = 0;
while(bfs(S,T)){
memcpy(cur+1,head+1,T<<2);
res += dfs(S,INF,T);
}
return res;
}
int maze[MAXN], bitcnt[1<<17];
int status[MAXN];
int main(){
for(int i=1; i!=(1<<17); ++i)
bitcnt[i] = (i&1)+bitcnt[i>>1];
for(int T=readint(); T; --T){
int A = readint(), B = readint(), C = readint();
{ // magic!
int ys[3] = {0,1,2}; // which is original dimension
const int len[3] = {A,B,C}; int i[3];
if(A > B) swap(A,B), swap(ys[0],ys[1]);
if(A > C) swap(A,C), swap(ys[0],ys[2]);
for(i[0]=0; i[0]!=len[0]; ++i[0])
for(i[1]=0; i[1]!=len[1]; ++i[1])
for(i[2]=0; i[2]!=len[2]; ++i[2])
maze[i[ys[0]]*B*C+i[ys[1]]*C+i[ys[2]]] = readint();
}
for(int j=0; j!=B; ++j) for(int k=0; k!=C; ++k)
for(int i=status[j*C+k]=0; i!=A; ++i)
if(maze[i*B*C+j*C+k]) status[j*C+k] ^= (1<<i);
const int sink = B*C+2; int ans = INF;
for(int S=0; S!=(1<<A); ++S){
memset(head+1,-1,sink<<2), cntEdge = 0;
for(int j=0; j!=B; ++j){
addEdge(sink-1,j+1,1);
for(int k=0; k!=C; ++k)
if((status[j*C+k]&S) != status[j*C+k])
addEdge(j+1,k+B+1,1);
}
for(int k=0; k!=C; ++k) addEdge(k+B+1,sink,1);
ans = min(ans,bitcnt[S]+dinic(sink-1,sink));
}
printf("%d\n",ans);
}
return 0;
}