[CTSC2007]挂缀 解题报告
来源:互联网 发布:flash插件 mac 编辑:程序博客网 时间:2024/05/16 01:08
时限:1s;内存限制:162MB
这真的是一道非常非常好的题,我做了很久很久。
在网上只能搜到只有结论的贪心题解和一篇关于这道题的非常简略的论文,导致我一直想不通为什么,不过还是很感谢论文里提供的思路,虽然我并不能看懂。。
论文名称叫作《浅谈信息学竞赛中的区间问题》,下文中将多有引用。
接下来我将详细地阐述和证明关于这道题的一些结论和做法,希望可以给以后做这些的人一些启发。
一、模型转换
虽然并没有什么本质用处,但为我们思考提供了很大方便。
每个挂缀实际上等价于一个区间,W是它的长度,C是至右左端点,将这些区间排列在数轴上,区间只能在端点处相交。
这样这个奇怪的问题就变成了我们所熟悉的问题。
二、前提结论
至少存在一个最优解,使得区间是按W+C顺序排列的。
假如说最优解的区间序列中存在一个关于W+C的逆序对,不妨设其右端点分别为
三、30分算法。
容易想到一个
O(N2) 的动态规划。将区间按Ci+Wi 的值从小到大排序,设f(i,j) ,为前i个区间,选取了j个区间后,最大右端点坐标的最小值。则对于任意一个合法的f(i,j) ,做下面的两个动态转移:
f(i,j)=min{
f(i−1,j), //第i个区间不选
(f(i−1,j−1)+Wi)[f(i−1,j−1)≤Ci] //选第i个区间。
}
状态数为O(N2) ,状态转移时间为O(1) ,故时间复杂度为O(N2) 。然而仅仅O(N2) 是不够的,还需要优化。
——《浅谈信息学竞赛中的区间问题》
四、满分算法。
先来看看论文里是怎么讲的:
平衡树/块链优化DP
设g(i,j)=f(i,j)−f(i,j−1) ,显然i不变时,g(i,j) ,随着j的增大而呈单调递增(如果g(i,j)<g(i,j−1) ,那么f(i,j−1) 肯定不是最大右端点坐标的最小值)。故每次从g(i) 递推g(i+1) 时,可以进行如下的两个操作:⑴找到两个位置,left=min{j|g(i,j)>Wi+1},right=max{j|f(i,j)≤Ci+1} ;⑵删除g(right+1) ,将g(left+1),g(left+2),...,g(right) 的值向后平移到g(left+2),g(left+3),...,g(right+1) ,并将g(left+1) 的值修改为Wi+1 。贪心
维护一个按Wi+Ci 排序的有序表,初始为空。将所有区间按长度Wi 从小到大排序,依次处理。处理某个区间时,若它能够放入有序表,则选择该区间并放入表中,否则不选择。最后的表即是要求的状态。
然而我比较傻逼,并没有看懂这是为什么,所以我按照我的思路来解释这个题。
按照傻逼惯用的思路,我首先打个表,发现在30分的DP中,如果f(i,j)是通过第二种转移(选择当前区间)转移过来的,那么
证明:
不妨采用数学归纳法。
开始的时候反正DP状态都是空集各种性质都好说,也就是说从自己能推出自己就OK了。
这个结论有一个显然的推论是它将导致g(i,j)不减,因为从g(i,j)的角度看它相当于是在最小的j使得
设k为最大的j使得f(i-1,j)≠+∞。
若
若
所以当f(i-1)符合结论的时候,f(i)也符合,命题得证。
虽然我上面的证明中说了很多显然。。但是其实我想了非常久,因为我比较傻逼,所以其实很多地方。。想到了比较显然,想不到还是非常不显然的!
这样的话我们就可以发现论文中的做法明显扯淡了,right根本就不用求。
而我们也就有另一个维护g(i)的方法了,注意到这一坨g(i)其实就是W,所以我们只需要把区间按
考虑如果
那么论文中的贪心是怎么回事呢?
其实与DP是等价的。
考虑我们刚才是怎么维护的。
其实就相当于是把论文中的贪心用线段树维护搞成了扫描线+堆,弹出最大者,就相当于是说在论文的贪心做法中,这个家伙会发现自己插不进去了。
五、需要注意的问题
1、一定要拍大数据!
2、这个题。。数据不知道怎么回事,从题面上看W和C都应该会爆int的。。
#include<cstdio>#include<cstdlib>#include<iostream>using namespace std;#include<algorithm>int heap[200005];pair<long long,unsigned> a[200005];char * cp=(char *)malloc(4000000);inline void in(int &x){ while(*cp<'0'||*cp>'9')++cp; x=0; while(*cp>='0'&&*cp<='9')x=x*10+(*cp++^'0');}inline void in(long long &x){ while(*cp<'0'||*cp>'9')++cp; x=0; while(*cp>='0'&&*cp<='9')x=x*10+(*cp++^'0');}inline void in(unsigned &x){ while(*cp<'0'||*cp>'9')++cp; x=0; while(*cp>='0'&&*cp<='9')x=x*10+(*cp++^'0');}int main(){ freopen("pendant.in","r",stdin); freopen("pendant.out","w",stdout); fread(cp,1,4000000,stdin); int N,i,now,next,L=0; long long W=0; in(N); for(i=N;i--;){ in(a[i].first),in(a[i].second); a[i].first+=a[i].second; } sort(a,a+N); for(i=0;i<N;){ heap[++L]=a[i].second; for(now=L,next=now>>1;next&&heap[now]>heap[next];now=next,next>>=1)swap(heap[now],heap[next]); W+=a[i].second; for(++i;i<N&&a[i].first-a[i].second<W;++i) if(a[i].second<heap[1]){ W=W+a[i].second-heap[1]; heap[1]=a[i].second; for(now=1,next=now<<1;next<=L;now=next,next<<=1){ if(next<L&&heap[next|1]>heap[next])++next; if(heap[now]>=heap[next])break; swap(heap[now],heap[next]); }; } } printf("%d\n%lld\n",L,W);}
- [CTSC2007]挂缀 解题报告
- 【ctsc2007】【挂缀】
- [CTSC2007]挂缀pendant
- 【贪心】【CTSC2007】【cogs1584】挂缀
- 【CTSC2007】挂缀 贪心
- COGS 1584. [CTSC2007]挂缀
- 1148: [CTSC2007]挂缀pendant
- [bzoj1148][CTSC2007]挂缀pendant
- 解题报告
- 解题报告
- 解题报告
- 解题报告
- 解题报告
- 解题报告
- 解题报告
- Antiprime解题报告
- expr解题报告
- 华容道解题报告
- UIlabel
- 黑马程序员——ios开发基础之C语言数据类型、运算符与输入输出
- bzoj-2151 种树
- 删除字符串中多余的空格
- COCI CONTEST #3 29.11.2014 T1 STROJOPIS
- [CTSC2007]挂缀 解题报告
- 为什么选择CocoaPods进行iOS代码的管理
- Caffe之mnist demo的配置和运行
- hdu5240 Exam
- java虚拟机Class格式与指令
- lintcode-最长公共子序列-77
- COCI CONTEST #3 29.11.2014 T2 DOM
- 主动缓和,化干戈为玉帛
- UIApplication 的学习总结