POJ1201/ZOJ1508/HDU1384 Intervals(spfa解差分约束问题)

题意是说给出一些闭区间,这些区间上整点可以选择放一个元素或者不放,但是每个区间都有一个下限,就是说你在这个区间里面的元素个数不能低于这个下限值。

最后要求出最少需要几个元素才能满足每个区间的要求。

建图(参见这里)之后可以转化成最长路问题。

另:差分约束中dist[ ]的初始化很有意思,比如你初始化的是 0 ,那么按照最长路更新dist[ ]数组,最后得到的就是大于 0 的最小值;如果按照最短路更新dist[ ]的话,最后结果是小于 0 的最大值。 ——LC

还有,针对这种差分约束问题建图转化成为最短路问题的处理,很多 blog 的建图都是为了队列入队操作时方便,加入了一个虚点,这个虚点和图中每一个点都连一条权值为 0 的边,开始寻找最长路时,首先把这个虚点入队。其实我们可以省略这个点,这样的话,我们需要手动把图中每个点入队,同时把他们的dist[ ]值更新成 0 ,听着有点麻烦是吧,不过的确是个省时间的办法,至于优化效果怎么样,就不好说了。  ——LC

我的代码:

#include<iostream>
#include<queue>
#include<cstdio>
#include<cstring>
#include<climits>
#define find_min(a,b) a<b?a:b
#define find_max(a,b) a>b?a:b
using namespace std;

const int N = 50010;

struct Edge{
	int s,e,v;
	int next;
}edge[N];

int n,e_num,p_num,vis[N],head[N],dist[N];
int left_x,right_x;

void AddEdge(int a,int b,int c){
	edge[e_num].s=a; 
	edge[e_num].e=b; 
	edge[e_num].v=c;
	edge[e_num].next=head[a]; 
	head[a]=e_num++;
}

queue <int>q;

void spfa(){
	while(!q.empty()){
		int cur=q.front();
		q.pop();
		vis[cur]=0;
		for(int i=head[cur];i!=-1;i=edge[i].next){
			int u=edge[i].e;
			if(dist[u] < dist[cur]+edge[i].v){
				dist[u]=dist[cur]+edge[i].v;
				if(!vis[u]){
					q.push(u);vis[u]=1;
				}
			}
		}
		if(cur>left_x && dist[cur]-1 > dist[cur-1]){
			dist[cur-1]=dist[cur]-1;
			if(!vis[cur-1]){
				q.push(cur-1);vis[cur-1]=1;
			}
		}
		if(cur<right_x && dist[cur] > dist[cur+1]){
			dist[cur+1]=dist[cur];
			if(!vis[cur+1]){
				q.push(cur+1);vis[cur+1]=1;
			}
		}
	}
	printf("%d\n",dist[right_x]);
}

void getmap(){
	int i,a,b,c;
	e_num=p_num=1;
	memset(head,-1,sizeof(head));
	memset(vis,0,sizeof(vis));
	
	left_x=INT_MAX; right_x=INT_MIN;
	for(i=1;i<=n;i++){
		scanf("%d%d%d",&a,&b,&c);
		AddEdge(a,b+1,c);//可能存在a==b的情况
		left_x=find_min(left_x,a);
		right_x=find_max(right_x,b+1);
	}

	//省略掉那个虚点,可以优化spfa.虚点作用是让所有点入队,且dist[]更新为0,
	//所以省略掉这点的话,需要手动把所有点入队,dist[]赋初值为0
	memset(dist,0,sizeof(dist));
	for(i=left_x;i<=right_x;i++){
		q.push(i);vis[i]=1;
	}
}
int main(){
	while(~scanf("%d",&n)){
		getmap();
		spfa();
	}
	return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值