洛谷 P2294 【[HNOI2005]狡猾的商人】

来一个思路比较暴力的题解吧。

暴力天下第一


思路: 这道题转化成差分约束的没想到后,我就想区间分割,把一个区间分割成两个小区间,如果两个小区间都被更新过了,那就拿来判断这两个小区间之和是否等于大区间。

具体实现:

分两步:

  1. 将这个区间当做一个大区间,把大区间依次分割成两个小区间,看两个小区间之和是否等于大区间。

  2. 将这个区间当做一个小区间,跟其他已知的可以合并的区间合并,当然,合并之前也要判断这个区间和可以与之合并的区间,合并成的大区间如果有被更新过,那就判断一下,如果没有更新过,那就更新一下。

一个疑惑:这时,肯定有人会问:为什么一个区间分成两个区间就行了?万一这个区间再分成两个小区间,这两个区间有更新过,那不是应该分成三个小区间来算了啊。我一开始也是有这样的疑惑,但是想一下,既然这个区间可以分为两个更新过的小区间了,那么在之前,我们肯定更新过这个区间了,所以不用担心分成两个小区间会错啦。

代码(注意下循环的细节):

#include 
using namespace std;
int n , m , T , f;
int dis[110][110];
int main(){
	cin >> T;
	while(T--){
		cin >> n >> m;
		for(int i = 1; i <= n; i++)
			for(int j = 1; j <= n; j++) dis[i][j] = 0x3ffffff;	//初始化!!!这个是判断区间更新过没的标志 
		f = 0;
		while(m--){
			int x , y , z;
			cin >> x >> y >> z;
			for(int i = x; i < y; i++){	//注意是x~y-1,因为两个分成的区间不能重合 
				if(dis[x][i] != 0x3ffffff && dis[i + 1][y] != 0x3ffffff && dis[x][i] + dis[i + 1][y] != z){
					f = 1;
					cout << "false" << endl;
					break;
				}
			}
			if(f) break;
			dis[x][y] = z;
			for(int i = 1; i < x; i++)	//这注意里的循环范围 
				if(dis[i][x - 1] != 0x3ffffff) 
					if(dis[i][y] != 0x3ffffff && dis[i][y] != dis[i][x - 1] + z) f = 1;
					else dis[i][y] = dis[i][x - 1] + z;
			if(f){
				cout << "false" << endl;
				break;
			}
			for(int i = y + 1; i <= n; i++) 	////这注意里的循环范围 
				if(dis[y + 1][i] != 0x3ffffff) 
					if(dis[x][i] != 0x3ffffff && dis[x][i] != dis[y + 1][i] + z) f = 1;
					else dis[x][i] = dis[y + 1][i] + z;
			if(f){
				cout << "false" << endl;
				break;
			}
		}
		if(f) continue;
		cout << "true" << endl;
	}
	return 0;
}

你可能感兴趣的:(洛谷 P2294 【[HNOI2005]狡猾的商人】)