题意是给你n个区间,n<=10^5,
每个区间有左右两个端点,a,b,a,b<=10^5
然后要你选一个点集,使这个点集在每个区间中至少有2两个点,求这个点集的点的个数的最小值
做法是贪心或者差分约束系统
贪心:就是说把所有区间按右端点升序排序,然后对于第一个区间,取最右边的那两个数,点数+2,因为越靠右越有可能被后面用到,从而使点数尽可能少
然后记录上次取的那两个数a1,a2,然后对于下一个区间,首先看看a1在不在这个区间中,在的话,这个区间肯定至少已经有两个数被取了,检查下个区间
如果a1不在,然后看看a2在不在,假如a2在的话,那就让a1=a2,a2为这个区间的最右端的值,点数+1
如果a2也不在,那a1和a2就是当前这个区间的最右端的两个数,点数+2
遍历一遍就可以
#include <string>
#include <iostream>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <vector>
#include <fstream>
#include <queue>
using namespace std;
#define ll long long
#define INF 0x3f3f3f3f
#define maxn 100005
struct Node
{
int l, r;
Node() :l(0), r(0){}
Node(int l, int r)
{
this->l = l; this->r = r;
}
bool operator <(Node &rhs)const
{
return r < rhs.r;
}
};
Node interval[maxn];
int n;
int main()
{
//freopen("input.txt","r",stdin);
//freopen("output.txt","w",stdout);
//ios::sync_with_stdio(false);
//cin.tie(0); cout.tie(0);
//ifstream in;
//in.open("input.txt", ios::in);
scanf("%d", &n);
int a, b;
int ans = 0;
for (int i = 0; i < n; ++i)
{
scanf("%d%d", &interval[i].l, &interval[i].r);
}
sort(interval, interval + n);
ans = 2; a = interval[0].r - 1; b = interval[0].r;
for (int i = 1; i < n; ++i)
{
if (b < interval[i].l)
{
a = interval[i].r - 1; b = interval[i].r;
ans += 2;
}
else if (a < interval[i].l)
{
a = b; b = interval[i].r;
++ans;
}
}
printf("%d\n", ans);
//while (1);
return 0;
}
差分约束:我是第一次写差分约束,就谈谈我浅显的理解吧
我们用sum[i]表示[0,i)中所取的点的个数,所以对于每个区间[a,b],有sum[b+1]>=sum[a]+2
但是这个约束条件还不够,这个约束条件只约束了一部分点,不连续
对于所有的点,sum[i+1]>=sum[i],sum[i+1]-sum[i]<=1,一个是表示后一个的肯定要比当前这个要大或者相等,另一个表示后一个比当前这个最多大1
然后这样,图就可以建了,
差分约束有两种,一种是>=的不等式,求的是最长路,另一种是<=的不等式,求的是最短路
最长路的:
dist[i]表示[0,i)中包含的数的个数
dist[b+1]>=dist[a]+2
dist[i+1]>=dist[i]
dist[i]>=dist[i+1]-1
dist[b+1]>=dist[a]+2
dist[i+1]>=dist[i]
dist[i]>=dist[i+1]-1
然后这个肯定是求最长路的,但是是从哪到哪的最长路呢?
首先我们就说我们涉及到的所有区间端点中 ,假设最小的为minv,最大的为maxv,
然后肯定起点从 minv就可以,[0,minv)肯定为0,终点为maxv即可,因为我们已经求出所有我们想求的点了
然后,这个差分约束的不等式dist[b+1]>=dist[a]+2,是从a到b+1的边,我们求的是到b+1的最长路,所以依次类推,我们从minv出发,求到maxv的最长路即可,dist[minv]初始为0即可
还有一个问题就是初始化的问题,一开始我也是把所有点初始化为0的,然后就WA了,初始化为-1之后才AC,
这个原因,对拍后自己手写了手写发现,是因为要让所有的约束都运行一遍,初始化为-1,所有的点都会跑一遍更新,所以所有的约束都会跑到,跑到最后就是最优解
如果初始化为0的话,可能有些不是从minv开始的约束,而是从中间某个点开始的约束,约束到了最终答案,然后这个约束就没有跑到,
所以用一个超级源点把所有点都连条边权为0的边也可以,然后从那个超级源点跑spfa
#include <string>
#include <iostream>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <vector>
#include <fstream>
#include <queue>
using namespace std;
#define ll long long
#define INF 0x3f3f3f3f
#define maxn 10005
#define maxm 300055
struct Edge
{
int next, to, w;
Edge() :next(0), to(0), w(0) { }
}edge[maxm];
int head[maxn], cnt = 0;
void add(int u, int v, int w)
{
edge[cnt].w = w;
edge[cnt].to = v;
edge[cnt].next = head[u];
head[u] = cnt++;
}
int n, m;
bool inq[maxn];
int dist[maxn];
bool SPFA(int s)
{
memset(inq, false, sizeof(inq));
memset(dist, -1, sizeof(int)*maxn);
queue<int> Q;
dist[s] = 0;
inq[s] = true;
Q.push(s);
while (!Q.empty())
{
int u = Q.front(); Q.pop();
int v;
inq[u] = false;
for (int i = head[u]; i != -1; i = edge[i].next)
{
//printf("u %d %d v %d %d edge %d\n", u, dist[u], edge[i].to, dist[edge[i].to], edge[i].w);
if (dist[u] + edge[i].w > dist[edge[i].to])
{
v = edge[i].to;
dist[v] = dist[u] + edge[i].w;
if (!inq[v])
{
Q.push(v);
inq[v] = true;
}
}
}
}
return true;
}
int main()
{
//freopen("input.txt","r",stdin);
//freopen("output.txt","w",stdout);
//ios::sync_with_stdio(false);
//cin.tie(0); cout.tie(0);
//ifstream in;
//in.open("input.txt", ios::in);
cnt = 0; memset(head, -1, sizeof(int)*maxn);
int a, b;
scanf("%d", &m);
n = 0;
int minv = maxn, maxv = 0;
for (int i = 0; i < m; ++i)
{
scanf("%d%d", &a, &b);
//dist[i]表示[0,i)中包含的数的个数
//dist[b+1]>=dist[a]+2
//dist[i+1]>=dist[i]
//dist[i]>=dist[i+1]-1
//>=就是求最长路,<=就是求最短路
add(a, b + 1, 2);
minv = min(a, minv); maxv = max(b + 1, maxv);
}
//printf("minv %d maxv %d\n", minv, maxv);
for (int i = minv; i < maxv; ++i)
{
add(i + 1, i, -1);
add(i, i + 1, 0);
}
SPFA(minv);
/*for (int i = minv; i <= maxv; ++i)
printf("%d ", dist[i]);
printf("\n");*/
printf("%d\n", dist[maxv]);
//while (1);
return 0;
}
最短路:
dist[i]表示[0,i)中包含的数的个数
dist[a]<=dist[b+1]-2
dist[i]<=dist[i+1]
dist[i+1]<=dist[i]+1
>=就是求最长路,<=就是求最短路
dist[a]<=dist[b+1]-2
dist[i]<=dist[i+1]
dist[i+1]<=dist[i]+1
>=就是求最长路,<=就是求最短路
然后所以这个约束就是求最短路,然后也与上面的类似,可以发现是从b+1到a的约束,所以要从maxv跑到minv,
但是还有一个问题就是,dist[maxv]初始化为0,表示起点为0,但是往后面更新呢,发现dist[i]是负数,这是什么情况呢?
后来想了想,发现,是这个边约束的是,前面的sum[i]比后面的少多少个点,其实我们最后得到的也是dist[maxv]-dist[minv],因为差分约束中,只跟边权有关系,而对于初始值,对于我们这种求解的个数的来说,没太大关系,所以这个的答案就是-dist[minv],然后也就可以了
#include <string>
#include <iostream>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <vector>
#include <fstream>
#include <queue>
using namespace std;
#define ll long long
#define INF 0x3f3f3f3f
#define maxn 10005
#define maxm 100005
struct Edge
{
int next, to, w;
Edge() :next(0), to(0), w(0) { }
}edge[maxm];
int head[maxn], cnt = 0;
void add(int u, int v, int w)
{
edge[cnt].w = w;
edge[cnt].to = v;
edge[cnt].next = head[u];
head[u] = cnt++;
}
int n, m;
bool inq[maxn];
int cntq[maxn];
int dist[maxn];
bool SPFA(int s)
{
memset(inq, false, sizeof(inq));
memset(cntq, 0, sizeof(int)*maxn);
memset(dist, 0x3f, sizeof(int)*maxn);
queue<int> Q;
dist[s] = 0;
inq[s] = true;
Q.push(s);
while (!Q.empty())
{
int u = Q.front(); Q.pop();
int v;
inq[u] = false;
for (int i = head[u]; i != -1; i = edge[i].next)
{
//printf("u %d %d v %d %d edge %d\n", u, dist[u], edge[i].to, dist[edge[i].to], edge[i].w);
if (dist[u] < INF&&dist[u] + edge[i].w < dist[edge[i].to])
{
v = edge[i].to;
dist[v] = dist[u] + edge[i].w;
if (!inq[v])
{
Q.push(v);
inq[v] = true;
}
}
}
}
return true;
}
int main()
{
//freopen("input.txt","r",stdin);
//freopen("output.txt","w",stdout);
//ios::sync_with_stdio(false);
//cin.tie(0); cout.tie(0);
//ifstream in;
//in.open("input.txt", ios::in);
cnt = 0; memset(head, -1, sizeof(int)*maxn);
int a, b;
scanf("%d", &m);
n = 0;
int minv = maxn, maxv = 0;
for (int i = 0; i < m; ++i)
{
scanf("%d%d", &a, &b);
//dist[i]表示[0,i)中包含的数的个数
//dist[a]<=dist[b+1]-2
//dist[i]<=dist[i+1]
//dist[i+1]<=dist[i]+1
//>=就是求最长路,<=就是求最短路
add(b + 1, a, -2);
minv = min(a, minv); maxv = max(b + 1, maxv);
}
//printf("minv %d maxv %d\n", minv, maxv);
for (int i = minv; i < maxv; ++i)
{
add(i, i + 1, 1);
add(i + 1, i, 0);
}
SPFA(maxv);
/*for (int i = minv; i <= maxv; ++i)
printf("%d ", dist[i]);
printf("\n");*/
printf("%d\n", -dist[minv]);
//while (1);
return 0;
}