题意:给你许多小区间[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;
}