差分约束题库

题目连接:intervals

题意:

给一个区间[ai,bi],这个区间里有ci个相同的元素,问这个集合最少包含多少元素;


思路:

可以得到这样的几个不等式;
c[i]<=dis[b+1]-dis[a];
0<=dis[i+1]-dis[i];
-1<=dis[i]-dis[i+1];
题意是一定要满足这些条件,那么就是找最长路;当然也可以变成 >= 来建图,求最短路;

 代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int INF = 2147483647;
const int maxn = 50050;
struct Node{
	int ne;
	int to;
	int w;
}e[maxn<<2];
int head[maxn];
int vis[maxn];
int dis[maxn];
int cnt,n,en,st;
void init(){
	memset(head,-1,sizeof(head));
	memset(vis,0,sizeof(vis));
	for(int i=0;i<=n;i++){
		dis[i] = -INF;
	}
	st = INF;
	cnt = en = 0;
}
void add(int u,int v,int val){
	e[cnt].to = v;
	e[cnt].ne = head[u];
	e[cnt].w = val;
	head[u] = cnt ++;
}
int SPFA(){
	queue<int>q;
	dis[st] = 0;
	vis[st] = 1;
	q.push(st);
	while(!q.empty()){
		int now = q.front();
		q.pop();
		for(int i=head[now];~i;i=e[i].ne){
			int to = e[i].to;
			if(dis[to] < dis[now] + e[i].w){
				dis[to] = dis[now] + e[i].w;
				if(!vis[to]){
					vis[to] = 1;
					q.push(to);
				}
			}
		}
		vis[now] = 0;
	} 
	return dis[en];
}
int main()
{
	while(scanf("%d",&n) != EOF){
		init();
		for(int i=1;i<=n;i++){
			int x,y,val;
			scanf("%d%d%d",&x,&y,&val);
			add(x,y+1,val);
			en = max(en,y+1);
			st = min(st,x);
		}
		for(int i=st;i<en;i++){
			add(i,i+1,0);
			add(i+1,i,-1);
		}
		printf("%d\n",SPFA());
	}
	return 0;
 } 

HDU 3592

include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int maxn = 10000;
const int INF = 2147483647;
struct Node{
	int to;
	int ne;
	int w;
}e[maxn];
int head[maxn];
int vis[maxn],dis[maxn],num[maxn];
int cnt,T,n,m,k;
void init(){
	memset(head,-1,sizeof(head));
	for(int i=0;i<=n;i++){
		vis[i] = 0;
		dis[i] = INF;
		num[i] = 0;
	}
	cnt = 0;
}
void add(int u,int v,int val){
	e[cnt].to = v;
	e[cnt].ne = head[u];
	e[cnt].w = val;
	head[u] = cnt ++;
}
int SPFA(){
	queue<int>q;
	q.push(1);
	dis[1] = 0;
	vis[1] = 1;
	while(!q.empty()){
		int now = q.front();
		q.pop();
		for(int i=head[now];~i;i=e[i].ne){
			int to = e[i].to;
			if(dis[to] > dis[now] + e[i].w){
				dis[to] = dis[now] + e[i].w;
				if(!vis[to]){
					q.push(to);
					if(++num[to] > n )
						return -1;
					vis[to] = 1;
				}
			}
		}
		vis[now] = 0;
	}
	return dis[n] == INF ? -2 : dis[n];
}
int main()
{
	scanf("%d",&T);
	while(T--){
		scanf("%d%d%d",&n,&m,&k);
		init();
		int x,y,val;
		for(int i=1;i<=m;i++){
			scanf("%d%d%d",&x,&y,&val);// y - x <= val
			add(x,y,val);
		}
		for(int i=1;i<=k;i++){
			scanf("%d%d%d",&x,&y,&val); // y - x >= val  => x - y <= -val
			add(y,x,-val);
		}
		int k = SPFA();
		printf("%d\n",k);
	} 
	return 0;
}

HDU 3440

题意:有n个屋子,超人从最矮的屋子开始,依次跳下比当前屋子高且最接近当前高度的屋子(即按照屋子高度增序来跳),但超人跳跃还有一个水平距离限制D,他每次跳的水平距离<=D。现在给你每个屋子的高度是它们的相对位置,你不能改变屋子的相对位置,但是可以水平移动屋子,使得最矮的屋子和最高的屋子的水平距离最大。如果无论怎样移动,超人都无法跳到最后那个屋子则输出-1

思路:用一个结构体记录房子的高度以及位置,id存房子的编号,high存房子的高度

建边一:相邻的房子至少距离为1(未排序之前)

建边二:将房子按高度递增排序后,选择 最低房子 和 最高房子中位置较小的为起点,另一个为终点。那么对于相邻的房子,我们可以建一条 位置小的指向位置大的边且权值为D;

(位置相邻的房子距离至少差1,相邻高度的两间房子最多差d来建边)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;

const int maxn = 1100;
const int INF = 2147483647;
struct Node{
	int to;
	int ne;
	int w;
}e[maxn<<2];
struct N{
	int id;
	int high;
}str[maxn];

int dis[maxn],vis[maxn],head[maxn],num[maxn];
int cnt ,T,n,d,st,en;
void init(){
	cnt = 0;
	memset(head,-1,sizeof(head));
}
bool cmp(N a,N b){
	return a.high > b.high;
}
void add(int u,int v,int val){
	e[cnt].ne = head[u];
	e[cnt].to = v;
	e[cnt].w = val;
	head[u] = cnt ++;
}
void get(){
	scanf("%d%d",&n,&d);
	for(int i=1;i<=n;i++){
		scanf("%d",&str[i].high);
		str[i].id = i;
	}
	sort(str+1,str+1+n,cmp);
	st = min(str[1].id,str[n].id);
	en = max(str[1].id,str[n].id);
	for(int i=1;i<=n-1;i++){
		if(str[i].id > str[i+1].id){
			add(str[i+1].id,str[i].id,d);
		}
		else {
			add(str[i].id,str[i+1].id,d);
		}
		add(i+1,i,-1);
	}
}
void SPFA(){
	for(int i=1;i<=n;i++){
		dis[i] = INF;
		vis[i] = 0;
		num[i] = 0;
	}
	queue<int>q;
	vis[st] = 1;
	dis[st] = 0;
	num[st] = 1;
	q.push(st);
	while(!q.empty()){
		int now = q.front();
		q.pop();
		for(int i=head[now];~i;i=e[i].ne){
			int to = e[i].to;
			if(dis[to] > dis[now] + e[i].w){
				dis[to] = dis[now] + e[i].w;
				if(!vis[to]){
					vis[to] = 1;
					q.push(to);
					if(++num[to] > n){
						printf("-1\n");
						return ;
					}
				}
			}
		}
		vis[now] = 0;
	}
	printf("%d\n",dis[en]);
}
int main(){
	scanf("%d",&T);
	int k = 1;
	while(T--){
		init();
		get();
		printf("Case %d: ",k++);
		SPFA();
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值