[hdu3440]House Man

House Man

题解

很经典的一道差分约束的题目。

我们现在有两个约束条件:

  • 相邻两点的距离大于1。
  • 两个高度紧挨着的点的距离不超过d。(注意,两个高度相邻的点之间如果有一个更高的他会直接跳过这个更高的,一脸懵*)

于是我们就得到了两个约束条件,设x_{i}表示第i个点的位置,a_{i}表示第i高的点的编号。

x_{i+1}-x_{i} \geq 1\left | x_{a_{i+1}}-x_{a_{i}}\right |\leq d

根据这两个条件差分约束建图,跑一遍spfa,注意判负环。

源码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
#define MAXN 2005
typedef long long LL;
typedef pair<int,int> pii;
const int INF=0x7f7f7f7f;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
int n,d,head[MAXN],tot,dis[MAXN],cnt[MAXN];
bool insta[MAXN],flag;
queue<int> q;
struct ming{int h,id;}a[MAXN];
struct edge{int to,nxt,paid;}e[MAXN<<1]; 
bool cmp(ming x,ming y){return x.h<y.h;}
void addEdge(int u,int v,int w){e[++tot]=(edge){v,head[u],w};head[u]=tot;}
int spfa(int s,int t){
	for(int i=1;i<=n;i++)dis[i]=INF,insta[i]=cnt[i]=0;
	while(!q.empty())q.pop();
	insta[s]=1;cnt[s]++;dis[s]=0;q.push(s);
	while(!q.empty()){
		int u=q.front();q.pop();insta[u]=0;
		for(int i=head[u];i;i=e[i].nxt){
			int v=e[i].to;
			if(dis[u]+e[i].paid<dis[v]){
				dis[v]=dis[u]+e[i].paid;
				if(!insta[v]){
					insta[v]=1;q.push(v);
					cnt[v]=cnt[u]+1;if(cnt[v]>n)return -1;
				}
			}
		}
	}
	return dis[t];
}
signed main(){
	int tt=0,t;read(t);
	while(t--){
		scanf("%d %d",&n,&d);
		memset(head,0,sizeof(head));tot=0;
		for(int i=1;i<=n;i++)read(a[i].h),a[i].id=i;
		sort(a+1,a+n+1,cmp);flag=0;
		for(int i=1;i<n;i++){
			addEdge(i+1,i,-1);
			int u=min(a[i].id,a[i+1].id),v=max(a[i].id,a[i+1].id);
			if(u-v>d){flag=1;break;}addEdge(u,v,d);
		}
		if(flag){printf("Case %d: -1\n",++tt);continue;}
		int res=spfa(min(a[1].id,a[n].id),max(a[1].id,a[n].id));
		if(res<0)printf("Case %d: -1\n",++tt);
		else printf("Case %d: %d\n",++tt,res);
	}
	return 0;
}

谢谢!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值