CF1332D Walk on Matrix

16 篇文章 1 订阅
5 篇文章 0 订阅

博客园同步

原题链接

90个测试点好评

简要题意:

求一个矩阵走过的最大 & \& & 和(只能往右、下走),然后有这样一个程序:

initialize   d p i , j ← 0 , for   all   0 ≤ i ≤ n , 0 ≤ j ≤ m , except  d p 0 , 1 ← a 1 , 1 \texttt{initialize} \space dp_{i,j} \gets 0 , \texttt{for all} \space 0 \leq i \leq n , 0 \leq j \leq m ,\text{except} \space dp_{0,1} \gets a_{1,1} initialize dpi,j0,for all 0in,0jm,except dp0,1a1,1

for   i = 1 → n   to   do \texttt{for} \space i =1 \rightarrow n \space \texttt{to} \space \texttt{do} for i=1n to do

for   j = 1 → m   to   do \texttt{for} \space j =1 \rightarrow m \space \texttt{to} \space \texttt{do} for j=1m to do

d p i , j ← max ⁡ ( d p i − 1 , j & a i , j , d p i , j − 1 & a i , j ) dp_{i,j} \gets \max(dp_{i-1,j} \& a_{i,j} , dp_{i,j-1} \& a_{i,j}) dpi,jmax(dpi1,j&ai,j,dpi,j1&ai,j)

end   for \texttt{end for} end for

end   for \texttt{end for} end for

S ← d p n , m S \gets dp_{n,m} Sdpn,m

需要你 hack \text{hack} hack 掉这个程序,即构造一组数据,使得正确答案比该程序的答案正好大 k k k.

首先我们要知道这个程序为什么是错的。

假设不需要相差 k k k,随便构造一组:

3 1
2 3
0 1

你可以看到,该程序的 dp \text{dp} dp 数组为:

1 1 1 2 2 2
1 1 1 d p 1 , 1 = 3 dp_{1,1} = 3 dp1,1=3 d p 1 , 2 = 3 & 1 = 1 dp_{1,2} = 3 \& 1 = 1 dp1,2=3&1=1
2 2 2 d p 2 , 1 = 3 & 2 = 2 dp_{2,1} = 3 \& 2 = 2 dp2,1=3&2=2 d p 2 , 2 = max ⁡ ( 2 & 3 , 1 & 3 ) = max ⁡ ( 2 , 1 ) = 2 dp_{2,2} = \max(2 \& 3 ,1 \& 3) = \max(2 , 1) = 2 dp2,2=max(2&3,1&3)=max(2,1)=2
3 3 3 d p 3 , 1 = 2 & 0 = 0 dp_{3,1} = 2 \& 0 = 0 dp3,1=2&0=0 d p 2 , 3 = max ⁡ ( 0 & 1 , 2 & 1 ) = max ⁡ ( 0 , 0 ) = 0 dp_{2,3} = \max(0 \& 1 , 2 \& 1) = \max(0 , 0) = 0 dp2,3=max(0&1,2&1)=max(0,0)=0

完全错误。因为 d p 2 , 2 dp_{2,2} dp2,2 保存了最优值,但是因为

1 & 1 > 2 & 1 1 \& 1 > 2 \& 1 1&1>2&1,所以 不满足最优子结构,即每个子结构最优不一定当前状态最优。

hack 也是一门艺术

那么如何让它正好相差 k k k 呢?

我们以上面的例子为例,构造 2 × 3 2 \times 3 2×3 的矩阵。

a b
c d
e f

首先我们让 e = 0 e=0 e=0,直接废掉 a → c → e → f a \rightarrow c \rightarrow e \rightarrow f acef 这条路。

(因为 k & 0 = 0 k \& 0 = 0 k&0=0,路径为 0 0 0 不可能被 dp \texttt{dp} dp 选或作为答案)

那么,还剩下 a → b → d → f a \rightarrow b \rightarrow d \rightarrow f abdf a → c → d → f a \rightarrow c \rightarrow d \rightarrow f acdf 两条路。

如何误导 dp \texttt{dp} dp 呢?hack怪癖:必须让它的答案和正确答案相差我固定的这个值

然后假设正确答案为 k k k,而它的答案为 0 0 0,并进一步设 f = k f = k f=k.

此时我们构造这样一组数据 ( t = log ⁡ 2 k t = \log_2^k t=log2k

2 t + 2 − 1 2^{t+2}-1 2t+21 k k k
2 t + 1 2^{t+1} 2t+1 2 t + 2 − 1 2^{t+2}-1 2t+21
0 0 0 k k k

下面介绍原因。在上图中:

a & b & d = a & d & b = b = k a \& b \& d = a \& d \& b = b = k a&b&d=a&d&b=b=k a & d = d a \& d = d a&d=d,而结果全是 1 1 1,所以 k k k 的原来 1 1 1 位被保留,而 0 0 0 仍是 0 0 0

a & c & d = a & d & c = 2 t + 1 a \& c \& d = a \& d \& c = 2^{t+1} a&c&d=a&d&c=2t+1

2 t + 1 2^{t+1} 2t+1在二进制中,为 1 1 1 后面 t + 1 t+1 t+1 0 0 0,比 k k k 多一位,那么错误的程序会选择 2 t + 1 2^{t+1} 2t+1.

然后 ( 2 t + 1 ) & k = 0 (2^{t+1}) \& k = 0 (2t+1)&k=0,而正确的答案是 k & k = k k \& k = k k&k=k.

hack人也要讲技巧对不对

你只要想,你的同学写这个程序A了题,然后你发现正解一定要把她 hack,然后这题就很有趣味不是么

l o g 2 k log_2^k log2k 正好是 c++ \text{c++} c++ 库函数。

时间复杂度: O ( 1 ) O(1) O(1).

实际得分: 100 p t s 100pts 100pts.

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;

inline int read(){char ch=getchar();int f=1;while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
	int x=0;while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}

int main(){
	int k=read(),t=log2(k);
	printf("3 2\n");
	printf("%d %d\n",(1<<(t+2))-1,k); //位运算 1<<n 就是 2 的 n 次方
	printf("%d %d\n",(1<<(t+1)),(1<<(t+2))-1);
	printf("0 %d\n",k);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值