差分约束 [HNOI2005]狡猾的商人(洛谷 P2294)

[HNOI2005]狡猾的商人

题目大意:

n 个月,m 个约束条件,判断最终是否产生矛盾;


因为这里不是 xix_ixi <= yjy_jyj + ckc_kck,而是 sumtsum_tsumt-sums−1sum_{s-1}sums1 = viv_ivi,所以单向连边并不满足条件,要正反双向连边,然后求最短路,判断是否有负环;

代码:

#include<bits/stdc++.h>
#define LL long long
#define pa pair<int,int>
#define ls k<<1
#define rs k<<1|1
#define inf 0x3f3f3f3f
using namespace std;
const int N=20100;
const int M=50100;
const int mod=1e9;
int n,m;
struct Node{
	int to,nex,w;
}edge[N*2];
int head[N],cnt,dis[N],vis[N],sum[N];
void add(int p,int q,int w){
	edge[cnt].w=w,edge[cnt].to=q,edge[cnt].nex=head[p],head[p]=cnt++;
}
bool spfa(){
	for(int i=0;i<=n+1;i++) dis[i]=2e9,sum[i]=0,vis[i]=0;
	dis[0]=0;
	queue<int>qu;qu.push(0);
	while(!qu.empty()){
		int p=qu.front();qu.pop();
		vis[p]=0;sum[p]++;
		if(sum[p]>=n) return false;
		for(int i=head[p];~i;i=edge[i].nex){
			int q=edge[i].to;
			if(dis[q]>dis[p]+edge[i].w){
				dis[q]=dis[p]+edge[i].w;
				if(!vis[q]) qu.push(q),vis[q]=1;
			}
		}
	}
	return true;
}
void init(){
	memset(head,-1,sizeof(head));
	cnt=0;
	for(int i=0;i<=2*m;i++) edge[i].nex=edge[i].to=edge[i].w=0;
}
int main(){
	int w;scanf("%d",&w);
	while(w--){
		scanf("%d%d",&n,&m);
		init();
		for(int i=1;i<=m;i++){
			int s,t,v;scanf("%d%d%d",&s,&t,&v);
			add(s,t+1,v),add(t+1,s,-v);
		}
		for(int i=1;i<=n+1;i++) add(0,i,0);
		if(!spfa()) printf("false\n");
		else printf("true\n");
	}
	return 0;
}

### HNOI2005 木梳问题解析 #### 背景描述 题目涉及给定一个长度为 L 的数组 A,其中每个元素代表木板下侧直线上某一点的高度。目标是在这些高度之间连接线段形成“木梳”,并使得形成的图形满足特定条件。 #### 解决方案概述 对于这个问题的一个重要观察是,在经过适当处理后的序列中不应存在连续三个位置上的数值呈现严格单调上升或下降的趋势[^4]。这意味着如果我们将原始数据通过某种方式转换成一个新的形式,则可以利用这一特性来简化求解过程。 #### 游程编码的应用 采用游程编码(Run-Length Encoding, RLE)技术对输入序列进行预处理是一个有效的策略。具体来说,可以通过遍历原数组并将相邻相同值合并的方式减少不必要的重复项数量。这样做不仅能够降低后续操作的时间复杂度,而且有助于更直观地理解和解决问题中的模式识别部分。 #### 动态规划方法 基于上述思路,一种可能的方法是使用动态规划算法。定义状态 dp[i][j] 表示考虑到第 i 个元素为止,并且最后一个被选中的元素与其前驱之间的关系为 j (即相等、增加或减少) 下所能达到的最大/最小代价。转移方程式可以根据当前元素与上一元素的关系来进行更新: - 如果 Ai == Ai−1 则继承之前的状态; - 若 Ai > Ai−1 只能从前向后增长的情况转移过来; - 否则从后者向前减小的情况转移而来; 需要注意的是,在实现过程中要特别小心边界条件以及初始化阶段设置合理的初始值以防止错误传播。 ```cpp #include <iostream> using namespace std; const int MAXN = 1e5 + 7; int a[MAXN], f[MAXN][3]; //f[i][0]:平,f[i][1]:升,f[i][2]:降 //...省略读入代码... for(int i=1;i<=n;++i){ for(int j=0;j<3;++j)f[i][j]=INT_MAX; if(a[i]==a[i-1]){ f[i][0]=min(f[i-1][0], min(f[i-1][1]+1, f[i-1][2]+1)); } else if(a[i]>a[i-1]){ f[i][1]=min(f[i-1][0], f[i-1][1]); } else{ f[i][2]=min(f[i-1][0], f[i-1][2]); } } cout<<min({f[n][0],f[n][1],f[n][2]}); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值