POJ 1201 Intervals (差分约束系统)

题意:给你许多小区间[ai,bi],并且指定每一个小区间内至少包含ci个数。然后求一个长度最小区间 z, 使得与每一个小区间的相同元素都>=ci。
题解:
1.假设区间z=[s,t], sum[i]表示[s,t] ∩ [s,i] 的元素个数。那么[ai,bi] >= ci 则可以表示为sum[bi]-sum[ai-1]>=ci。
2.若i<=j,则 [s,t] ∩ [s,i] 的元素个数 <= [s,t] ∩ [s,j] 的元素个数,即:sum[i] <= sum[j]
3.若i<=j,则 sum[j] - sum[i] <= j-i
但是在本题中 0 <= ai <= bi  若用sum[ai-1]可能会产生下标位-1这样的情况,而这显然是不行,所以用sum[bi+1]-sum[ai]。(注意:要始终保持半开半闭区间这样的形式,避免边界处有重复计算)。

//AC
#include <iostream>
using namespace std;

#define N  100009
#define INF 999999

bool mark[N];
int head[N], dis[N], que[N];
int m, size, lmin, rmax;

struct Edge { int v, w, next; };
Edge  edge[N*3];

void Spfa()
{
	memset(mark,0,sizeof(mark));
	for ( int i = lmin; i <= rmax; i++ )
		dis[i] = INF;

	int u, v, front, rear;
	front = rear = 0;
	que[rear] = rmax+1;
	rear = ( rear + 1 ) % N;
	mark[rmax+1] = true;
	dis[rmax+1] = 0;

	while ( front != rear )
	{
		u = que[front];
		front = ( front + 1 ) % N;
		mark[u] = false;
		for ( int i = head[u]; i; i = edge[i].next )
		{
			v = edge[i].v;
			if ( dis[v] > dis[u] + edge[i].w )
			{
				dis[v] = dis[u] + edge[i].w;
				if ( ! mark[v] )
				{
					que[rear] = v;
					rear = ( rear + 1 ) % N;
					mark[v] = true;				
				}
			}
		}
	}
}

void add ( int u, int v, int w )
{
	size++;
	edge[size].v = v;
	edge[size].w = w;
	edge[size].next = head[u];
	head[u] = size;
}

int main()
{
	int a, b, c, i;
	scanf("%d",&m);
	size = 0;
	lmin = INF; rmax = -INF;
	memset(head,0,sizeof(head));
	
	while ( m-- )
	{
		scanf("%d%d%d",&a,&b,&c);
		add ( b+1, a, -c );
		if ( a < lmin ) lmin = a;
		if ( b+1 > rmax ) rmax = b+1;		
	}
	
	for ( i = lmin; i < rmax; i++ )
	{
		add ( i+1, i, 0 );
		add ( i, i+1, 1 );
	}
	
	for ( i = lmin; i <= rmax; i++ )
		add ( rmax+1, i, 0 );
	
	Spfa();
	printf("%d\n",dis[rmax]-dis[lmin]);
	
	return 0;
}


下面再给出一篇代码,对比之下我发现一个问题。
1.上面的代码添加了超级源点,所以可以保证图的连通性,解出来的答案也没问题。
2.而下面的代码在没有添加源点的情况下也是连通的。因为从lmin到人rmax两两相邻的点之间都添加了边。但是如果我运行spfa的时候起点是lmin则结果错误,起点是rmax则结果正确。之后作图分析了下,发现这个加边的方向有关系,因为我加边是add(b+1,a,-c),如果以lmin为起点的话,很多加上的边起不到跟新的作用,换句话说,这些边都被 add ( i , i +1 , 1 )这样的边覆盖了。把样例画图表示出来便很好理解了。
//AC
#include <iostream>
using namespace std;

#define N  100009
#define INF 999999

bool mark[N];
int head[N], dis[N], que[N];
int m, size, lmin, rmax, s;

struct Edge { int v, w, next; };
Edge  edge[N*3];

void Spfa()
{
	memset(mark,0,sizeof(mark));
	for ( int i = lmin; i <= rmax; i++ )
		dis[i] = INF;

	s = rmax;              // 换成lmin则错误 !!!
	int u, v, front, rear;
	front = rear = 0;
	que[rear] = s;
	rear = ( rear + 1 ) % N;
	mark[s] = true;
	dis[s] = 0;

	while ( front != rear )
	{
		u = que[front];
		front = ( front + 1 ) % N;
		mark[u] = false;
		for ( int i = head[u]; i; i = edge[i].next )
		{
			v = edge[i].v;
			if ( dis[v] > dis[u] + edge[i].w )
			{
				dis[v] = dis[u] + edge[i].w;
				if ( ! mark[v] )
				{
					que[rear] = v;
					rear = ( rear + 1 ) % N;
					mark[v] = true;				
				}
			}
		}
	}
}

void add ( int u, int v, int w )
{
	size++;
	edge[size].v = v;
	edge[size].w = w;
	edge[size].next = head[u];
	head[u] = size;
}

int main()
{
	int a, b, c, i;
	scanf("%d",&m);
	size = 0;
	lmin = INF; rmax = -INF;
	memset(head,0,sizeof(head));
	
	while ( m-- )
	{
		scanf("%d%d%d",&a,&b,&c);
		add ( b+1, a, -c );
		if ( a < lmin ) lmin = a;
		if ( b+1 > rmax ) rmax = b+1;		
	}
	
	for ( i = lmin; i < rmax; i++ )
	{
		add ( i+1, i, 0 );
		add ( i, i+1, 1 );
	}
	
//	for ( i = lmin; i <= rmax; i++ )
	//	add ( rmax+1, i, 0 );
	
	Spfa();
	printf("%d\n",dis[rmax]-dis[lmin]);
	
	return 0;
}




你可能感兴趣的:(c,BI,Intervals)