POJ 1083
期间用了DP(可能更有些贪心的思想的算法),但是一个细节错误一直没有考虑
总体思路是这样的,按照每个区间的上界排序,然后从头到尾,定义\(dv(i)\)为前i个最少需要的并行数量
每次状态转移,从后向前搜索,每当发现当前区间\(i\)的下界比之前的区间的上界\(seg[j].e\)要小,说明二者必定重叠,直到遇到第一个不重叠的,把这第一个当作“父节点”,并更新父节点的上界。
这种思路大体是对的,但是有一个缺陷,就是随着父节点不断更新,最早加入的子节点有可能不知道父节点的上界会长大到什么程度,导致之后一些节点实际上已经不能加入他们所在组,但是因为只看子节点看不到这些局限,这样就漏掉了好多情况。
解决方案也显而易见,利用并查集
#include <iostream>
#include <algorithm>
#include <queue>
#include <string>
#include <vector>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <string>
#include <stack>
#include <map>
#include <set>
using namespace std;
const int maxn= 205;
struct Seg
{
int s, e;
Seg(int _s= 0, int _e= 0) : s(_s), e(_e) {}
bool operator < (const Seg &rhs) const
{
return e< rhs.e;
}
}segs[maxn];
int dv[maxn], fa[maxn];
inline void Init(int n)
{
for (int i= 0; i< n; ++i){
fa[i]= i;
}
}
inline int Find(int x)
{
if (x== fa[x]){
return x;
}
return fa[x]= Find(fa[x]);
}
int main()
{
int kase, n;
int a, b;
scanf("%d", &kase);
while (kase--){
scanf("%d", &n);
for (int i= 0; i< n; ++i){
scanf("%d %d", &a, &b);
if (a> b){
swap(a, b);
}
a= (a+1)>>1;
b= (b+1)>>1;
segs[i].s= a;
segs[i].e= b;
}
Init(n);
sort(segs, segs+n);
dv[0]= 1;
for (int i= 1; i< n; ++i){
int flag= 1;
for (int j= i-1; j>= 0; --j){
int fa_j= Find(j);
if (segs[i].s > segs[fa_j].e){
segs[fa_j].e= segs[i].e;
fa[i]= fa_j;
flag= 0;
break;
}
}
dv[i]= flag ? dv[i-1]+1 : dv[i-1];
}
printf("%d\n", dv[n-1]*10);
}
return 0;
}
后面参考了别人的代码,发现另一种方法才更适合DP,完成之后记录如下
#include <iostream>
#include <algorithm>
#include <queue>
#include <string>
#include <vector>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <string>
#include <stack>
#include <map>
#include <set>
using namespace std;
const int maxn= 205;
int ov[maxn];
int main()
{
int kase, n;
int a, b;
scanf("%d", &kase);
while (kase--){
scanf("%d", &n);
memset(ov, 0, sizeof(ov));
int ans= 0;
for (int i= 0; i< n; ++i){
scanf("%d %d", &a, &b);
if (a> b){
swap(a, b);
}
a= (a+1)>>1;
b= (b+1)>>1;
for (int i= a; i<= b; ++i){
++ov[i];
ans= max(ans, ov[i]);
}
}
printf("%d\n", ans*10);
}
return 0;
}