题意:
房间之间通过单通道抬桌子,一共有400个房间,分别按照图上顺序编号。每次交换需要10分钟,当两个交换路径不重叠时可以在同一个时间间隔内完成。给出所有的搬动需求,问最少需要的时间。
思路:
最开始的思路就是模拟,首先把两边归为一边,按起点将所有交换排序后,每次都贪心的拿一遍,这样最后可以得到拿的次数。因为数据量不大,即便是O(n^2)的时间复杂度也不会超时,所以很快写出来了。但是却WA的很惨。。。虽然对贪心的思路不太确定,但是没找到反例。
后来参考了网上的做法,基本上都是遍历每一个交换区间,用一个数组记录过道i被遍历的次数,这样遍历次数最多的那个区间的次数乘以10,就是最少需要的时间。这个思路的证明可以参考这个回答:http://poj.org/showmessage?message_id=347563
这个思路确实十分巧妙,但是我还是一直在纠结我的算法为什么会错。尝试着证明了一下,发现对于经过次数最多的那个过道,我的每一次遍历也是一定经过它的,并且遍历的次数也不可能小于经过这个过道的次数(每次最多拿一个),所以也就是说遍历的次数一定等于经过次数最多的过道的次数,也就是说两个算法是等价的。后面经过调试发现思路确实是对的,WA在了中间对于每次遍历点的标记。
这道题收获还是很多的,坚持了自己的算法没有盲从,并且最后通过调试得到了证明。两个算法在时间复杂度上想通,我的空间复杂度可能更到一些,但是思路更加直接,如果是比赛的时候的话还是直接点好吧。
PS:中间cb罢工无法调试,搞了半天解决不了,结果搁置了两天自己好了。。。终于把这道题给debug了出来。
代码实现:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
struct Node{
int sta;
int en;
};
const int MAX = 210;
bool cmp(const Node& n1, const Node& n2){
if( n1.sta != n2.sta ){
return n1.sta<n2.sta;
}
return n1.en<n2.en;
}
int T;
int N;
int res;
Node lis[MAX];
bool flag[MAX];
int path[MAX];
int main(){
scanf("%d",&T);
while( T-- ){
scanf("%d",&N);
int a,b;
res = 0;
memset(flag,false,sizeof(flag));
for( int i = 0; i < N; i++ ){
scanf("%d%d",&a,&b);
if( a%2 == 0 ){
a /= 2;
}
else{
a = (a-1)/2+1;
}
if( b%2 == 0 ){
b /= 2;
}
else{
b = (b-1)/2+1;
}
if( a > b ){
swap(a,b);
}
lis[i].sta = a;
lis[i].en = b;
}
int pos = 0;
int dis = 0;
sort(lis,lis+N,cmp);
for( int i = 0; i < N; i++ ){
if( flag[i] == true ){
continue;
}
pos = i+1;
flag[i] = true;
dis = lis[i].en;
res++;
while( pos < N ){
if( flag[pos] == true ){
pos++;
continue;
}
if( lis[pos].sta > dis ){
flag[pos] = true;
dis = lis[pos].en;
}
pos++;
}
}
printf("%d\n",res*10);
}
return 0;
}
测试样例:
http://poj.org/showmessage?message_id=119066