[FJOI2018]所罗门王的宝藏

Description

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

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

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

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


Solution

题外话

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

不会markdown求谅解QAQ


前置芝士
差分约束

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


正题

设第i行的操作使这一行的数增加了 X i X_i Xi,第j列的操作使这一列的数减少了 Y j Y_j Yj
那么显然对于第i行第j列绿宝石的密码c,有 X i X_i Xi- Y j Y_j Yj=c。
显 然 可 以 转 化 为 { X i − Y j &lt; = c X i − Y j &gt; = c 显然可以转化为 \left \{ \begin{array}{ll} X_i-Y_j&lt;=c\\ X_i-Y_j&gt;=c \end{array} \right. {XiYj<=cXiYj>=c
再 化 为 差 分 形 式 { X i − Y j &lt; = c Y j − X i &lt; = − c 再化为差分形式 \left \{ \begin{array}{ll} X_i-Y_j&lt;=c\\ Y_j-X_i&lt;=-c \end{array} \right. {XiYj<=cYjXi<=c
X i X_i Xi Y j Y_j Yj的边权为c的边和 Y j Y_j Yj X i X_i Xi的边权为-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!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值