题目为 light oj 1017.
现在是凌晨两点二十分,我却毫无睡意,这题折腾了我一个晚上,一直没有做对,最后发现转移方程忽略了一个重要的条件,泪奔~。
干脆不睡觉,写一篇题解警醒自己,也算是对于自己考虑问题智障的惩罚。
我真是个智障 0 s 0 .....
题目大意是 , 给你N个二维坐标上的点 (x,y) , 每一个点代表一个污渍,现在有一把宽度为 W 的刷子,平行于 x 轴移动 K 次,问,最多能擦掉多少污渍。
很明显这题和x坐标一点关系都没有(因为刷子沿着平行x轴移动,并可以移动无限远),分析y坐标就OK。 于是纪录y坐标以后排序,假设数组是 point[1...n]。直接DP因为点范围太大的缘故没有办法,所以先进行离散化,decode[1...len] 存放离散化以后的点的y轴坐标。 OK,然后就是很容易想到的转移情况。
哈哈哈哈哈哈哈,简直欣喜若狂有没有,有没有! 居然如此有思路,难得难得。。。。
dp[i][j] 表示,我擦完第i次以后,擦过decode[1...j](包含j) 这些污渍以后的最大擦去的数目,注意不一定是擦掉了所有的污渍(后面我就错在这里!!! T ^ T)
转移方程很明显,对于每一种情况j,考虑为擦除的“上界” 那么下界就是 pre = lower_bound(decode,decode + len,decode[j] - W); 转移的情况就是
dp[i][j] = max(dp[i][j] , dp[i - 1][pre - 1] + sum); (pre - 1 >= 0)
dp[i][j] = max(dp[i][j] , sum); (pre - 1 < 0)
因为用了lower_bound 函数,所以如果搜索的这个值小于decode的最小值的话,就会是0,pre - 1失去意义,所以分类下。
这里的sum很明显就是上下界(包含上下界)里的所有的污渍了。
那么上界的位置在
upper_bound(point , point + n , decode[j]) - point;
下界位置在
lower_bound(point , point + n , decode[j] - w) - point;
sum = 上界 - 下界;
然后!!!
然后!!!!
是不是好像就OK了!!
但是......
但是老娘的AC呢!!!!
并不是,痛苦的WA开始了,不停的错错错错错错错,想要撞死电脑有没有,有没有........
我靠这还有什么情况啊~。
不做出来就不睡觉了。。。。
老娘和你没完!!!
终于在凌晨一点半左右开窍了。
发现了转移方程的纰漏,如果清理污渍的时候分成了两段,中间有污渍“不选”的时候会产生最优解,那么转移的时候就会错误。
什么意思呢,也就是说,我们的方程dp[i][j] 表示的是清理了i次以后,高度为j污渍以下的污渍都已经考虑过了,这以后的最大值,才有转移的意义。
就是说 dp[i][j] = max{dp[i][1....j]}
所以在每一次转移完 dp[i][j] 以后,需要看看这个dp[i][j] 是不是当前 i , j 的最优解,也就是加上一句。
dp[i][j] = max(dp[i][j] , dp[i][j - 1]);
万一 dp[i][j - 1] 更好呢? 万一 高度为j 的污渍我不要,清理的能更多呢?
终于对了!
老娘终于可以睡觉了!!!
卧槽,三点了。。。。。。 Orz ........
代码君:
1 /* 2 3 light oj 1017 4 5 */ 6 7 8 #include <iostream> 9 #include <cstdio> 10 #include <cstring> 11 #include <algorithm> 12 using namespace std; 13 int point[110]; 14 int decode[110]; 15 int dp[110][110]; 16 int _decode(int n){ 17 int p = 1; 18 int pre = point[0]; 19 decode[0] = point[0]; 20 for (int i = 1; i < n; i++) { 21 if(pre == point[i]) continue; 22 decode[p++] = point[i]; 23 pre = point[i]; 24 } 25 return p; 26 } 27 int main(){ 28 int T; 29 scanf("%d",&T); 30 for (int tt = 1; tt <= T ; tt++) { 31 int n , w , k; 32 scanf("%d %d %d",&n,&w,&k); 33 for (int i = 0; i < n; i++) { 34 int temp; 35 scanf("%d %d", &temp ,point + i); 36 } 37 sort(point , point + n); 38 int len = _decode(n); 39 int MIN = point[0]; 40 memset(dp,0,sizeof(dp)); 41 for (int i = 1; i <= k; i++) { 42 for (int j = 0; j < len; j++) { 43 int pre = lower_bound(decode,decode + len,decode[j] - w) - decode; 44 int l = lower_bound(point , point + n , decode[j] - w) - point; 45 int r = upper_bound(point , point + n , decode[j]) - point; 46 if(pre - 1 < 0) dp[i][j] = max(dp[i][j] , r - l); 47 else dp[i][j] = max(dp[i][j] , dp[i - 1][pre - 1] + (r - l)); 48 // 49 dp[i][j] = max(dp[i][j] , dp[i][j - 1]); 50 // 关键部分 51 } 52 } 53 int ans = 0; 54 ans = dp[k][len - 1]; 55 cout << "Case " << tt << ": " << ans << endl; 56 } 57 return 0; 58 }