0%

思路讲解

唉,dp,你说没想到吧,也想到了一点,但没往下深想。

唉,主要想错了,这个行和列是相互独立的。

其实就是出现两者之间相差一的情况,那么这个比较小的列就无法加一了。

image

但其实上面这个说法不全面,导致我的算法设计有瑕疵,比如说下面这个例子

1
2
3
4
5
6
7
8
9
1
4
3 1 1 3 3 2 4 2
4 5 4 5 -> 4 6 -> 5 6
3 1 3 5 3 2 4 2
4 3 2 1 4 4 5 4
5 1 5 2
4 9 1 9

不难发现,上面这个例子还有可能传递,如第 nn 列需要 +1+1 但是和第 n1n-1 列相同了,所以第 n1n-1 列也需要 +1+1 ,但 n1n-1 列和第 n2n-2 列相同了……。

当然,上面说了这么多,其实意思只有一个,就是不能以需要操作的行和列为主体,而应该以所有列为主体,因为操作行和列的操作可能导致其他行和列也需要操作。

不过,我们也发现行和列能怎么操作也只和相邻的行和列相关,那么如果按顺序转移也是可以的。

状态定义如下所列。

1
2
3
// dpC[i][0] 表示保留第i列所需的最小花费
// dpC[i][1] 表示对第i列+1所需的最小花费
ll dpC[MAXN][2],dpR[MAXN][2];

转移只要确定是否合法其实就很简单了。

AC代码

https://codeforces.com/contest/2096/submission/318683142

心路历程(WA,TLE,MLE……)

思路讲解

赛时差一点想到AC,主要是needVal为负数的情况没有想到(减着减着变负数了)

其实只改一个数就行,其他数全部赋为-INF。

AC代码

https://codeforces.com/contest/2107/submission/318579760

心路历程(WA,TLE,MLE……)

思路讲解

1
2
3
4
// 要求你构造一个数组,符合A中的要求,如果A[1]=1,那么就说明这个
// 构造数组中的这个元素需要于第一次操作时去除。A[2]=-1,说明第二个元素被剩下了。
// 奇数次操作只留下局部最小值,偶数次操作只留下局部最大值(严格)。

AC代码

https://codeforces.com/contest/2103/submission/318747498

心路历程(WA,TLE,MLE……)

思路讲解

首先,注意到,这道题目我们是不在意这个段的中位数到底是几的,我们只在意这个段的中位数是 >K>K还是 K≤K 。因此我们只需要通过记录 K≤K 的数的数量,就可以知道符不符合要求。

那么前缀和后缀可以用这种方法比较简便的求得,那么中间的怎么办呢?

以前缀为例,在处理的时候,直接判断该位置和最近的前缀合法位置能不能组合为两个合法段。(贪心地,我们认为总是和最近的段组合为两个合法段)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
vector<ll> pres;
FOR(i,1,N){
preA[i]=preA[i-1];
if(A[i]<=K){
++preA[i];
}
if(!pres.empty()){
// 将这个点和之前合法的前缀组合一下,看看呢能不能凑出两个合法段
ll len=i-pres.back();
ll num=preA[i]-preA[pres.back()];
if(num>=ceil(len/2.0)){
cout<<"YES\n";
return;
}
}
if(preA[i]>=ceil(i/2.0l)){
pres.pb(i);
}
}

AC代码

https://codeforces.com/contest/2103/submission/318394685

心路历程(WA,TLE,MLE……)

思路讲解

jiangly的代码利用了状压dp的思想,我汲取一下思想,来写一波

AC代码

https://atcoder.jp/contests/abc404/submissions/65496213

心路历程(WA,TLE,MLE……)