[FJOI2018]所罗门王的宝藏

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/tylon2006/article/details/94487524

Description

据古代传说记载,所罗门王既是智慧的代表,又是财富的象征。他建立了强大而富有的国家,聚集了大批的黄金象牙和钻石,并把这些价值连城的珍宝藏在一个神秘的地方,这就是世人瞩目的“所罗门王的宝藏”。多少个世纪以来,人们一直在寻找这批早已失落的古代文明宝藏,寻找盛产黄金和钻石的宝地。曾经追寻所罗门王宝藏的冒险者们都一去不回,至今没人解开这个谜题。亨利男爵在一次幸运的旅途中意外地得到了三百年前一位葡萄牙贵族留下的写在羊皮卷上的所罗门王的藏宝图和一本寻宝秘籍。在这张藏宝图的诱惑下,亨利男爵邀请约翰上校和勇敢的猎象人夸特曼开始了寻找埋葬在黑暗地底的所罗门王宝藏的艰险历程。他们横穿渺无边际的沙漠和浓荫蔽日的原始森林,越过汹涌澎湃的激流险滩,翻越高耸入云的峻岭雪山,饱尝沙漠的酷热和冰雪严寒,在藏宝图的指引下来到非洲一个原始的神秘国度库库安纳。这里有残酷的人殉制度,有一个拥有一千个妻室的独眼暴君特瓦拉,有像兀鹫一般丑恶诡诈老而不死的女巫加古尔,还有美丽聪慧的绝代佳人弗拉塔。在这片陌生而又险象环生的土地上三位寻宝英雄历尽艰辛,终于在绝代佳人弗拉塔的帮助下在海底深处找到了珍藏这批价值连城宝藏的巨大的藏宝洞。然而在女巫加古尔的精心策划下,一场灭顶之灾正在悄悄逼近。

藏宝洞的洞门十分坚固且洞门紧闭,如果不知道开启洞门的秘密是无法打开藏宝洞的洞门。在藏宝洞的洞门一侧有一个奇怪的矩形密码阵列。根据寻宝秘籍的记载,在密码阵列每行的左侧和每列的顶端都有一颗红宝石按钮。每个按钮都可以向左或向右转动。每向左转动一次按钮,相应的行或列中数字都增 1。每向右转动一次按钮,相应的行或列中数字都减 1。在矩形密码阵列的若干特定位置镶嵌着绿宝石。只有当所有绿宝石位置的数字与藏宝图记载的密码完全相同,紧闭的洞门就会自动缓缓打开。女巫加古尔早已得知开门的秘密。为了阻止寻宝者打开洞门,女巫加古尔为开门的密码阵列设置了全 0 的初始状态。试图打开洞门的寻宝者如果不能迅速转动按钮使所有绿宝石位置的数字与藏宝图记载的密码完全相同,就会自动启动藏宝洞玄妙的暗器机关,使寻宝者遭到灭顶攻击而死于非命。

您能帮助三位寻宝英雄顺利打开藏宝洞的洞门吗?

编程任务:对于给定的密码阵列,找到获得正确密码的红宝石按钮的转动序列。


Solution

题外话

困扰了我很久,主要是因为对差分约束没什么印象,我还是肽蒻了QAQ

不会markdown求谅解QAQ


前置芝士

差分约束

专门解决给一些乱七八糟的不等式,再求某个点的最小/大值问题。
这里假设大家都会。


正题

设第i行的操作使这一行的数增加了 X i X_i ,第j列的操作使这一列的数减少了 Y j Y_j
那么显然对于第i行第j列绿宝石的密码c,有 X i X_i - Y j Y_j =c。
{ X i Y j < = c X i Y j > = c 显然可以转化为 \left \{ \begin{array}{ll} X_i-Y_j<=c\\ X_i-Y_j>=c \end{array} \right.
{ X i Y j < = c Y j X i < = c 再化为差分形式 \left \{ \begin{array}{ll} X_i-Y_j<=c\\ Y_j-X_i<=-c \end{array} \right.
X i X_i Y j Y_j 的边权为c的边和 Y j Y_j X i X_i 的边权为-c边,再跑最短路判负环即可。
因为这道题只考察解的存在与否,所以跑最长路也是可以的。


代码

Talk is free,show me the code.

#include<bits/stdc++.h>
using namespace std;
int t,n,m,k,s;
struct edge{
	int to,dis,nxt;
}e[4040];
int head[2020],tot;
void add(int x,int y,int c){
	e[++tot].to=y;
	e[tot].dis=c;
	e[tot].nxt=head[x];
	head[x]=tot;
}
int dis[2020];//距离
int vis[2020];//存储访问次数
bool in[2020];//是否入队
queue<int>q;
bool spfa(){
	for(int i=1;i<=n+m;i++)
	dis[i]=2e9,in[i]=vis[i]=0;
	dis[s]=0,vis[s]=1,q.push(s);
	while(!q.empty()){
		int u=q.front(); in[u]=0; q.pop();
		for(int i=head[u];i!=-1;i=e[i].nxt){
			int v=e[i].to,d=e[i].dis;
			if(dis[v]>dis[u]+d){
				dis[v]=dis[u]+d;
				if(!in[v]){
					vis[v]++;
					if(vis[v]>=n+m)//每个点最多访问n+m-1次,超过即有负环
					return false;
					in[v]=1;
					q.push(v);
				}
			}
		}
	}
	return true;
}
int main(){
	scanf("%d",&t);
	while(t--){
		int x,y,c;
		scanf("%d%d%d",&n,&m,&k);
		s=n+m+1,tot=0,head[s]=-1;//s为超极源点,因为可能图不连通,所以s向每个点连边
		for(int i=1;i<=n+m;i++)
		head[i]=-1,add(s,i,0);//记得清空
		for(int i=1;i<=k;i++){
			scanf("%d%d%d",&x,&y,&c);
			add(x,y+n,c),add(y+n,x,-c);
		}
		bool ok=spfa();
		if(ok) printf("Yes\n");
		else printf("No\n"); 
	}
} 

Good Luck!

猜你喜欢

转载自blog.csdn.net/tylon2006/article/details/94487524