POJ 1716 Integer Intervals 差分約束

題意是給你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
然後這個肯定是求最長路的,但是是從哪到哪的最長路呢?
首先我們就說我們涉及到的所有區間端點中 ,假設最小的爲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
>=就是求最長路,<=就是求最短路
然後所以這個約束就是求最短路,然後也與上面的類似,可以發現是從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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章