[FJOI2018]所羅門王的寶藏

[FJOI2018]所羅門王的寶藏

據古代傳說記載,所羅門王既是智慧的代表,又是財富的象徵。他建立了強大而富有的國家,聚集了大批的黃金象牙和鑽石,並把這些價值連城的珍寶藏在一個神祕的地方,這就是世人矚目的“所羅門王的寶藏”。多少個世紀以來,人們一直在尋找這批早已失落的古代文明寶藏,尋找盛產黃金和鑽石的寶地。曾經追尋所羅門王寶藏的冒險者們都一去不回,至今沒人解開這個謎題。亨利男爵在一次幸運的旅途中意外地得到了三百年前一位葡萄牙貴族留下的寫在羊皮捲上的所羅門王的藏寶圖和一本尋寶祕籍。在這張藏寶圖的誘惑下,亨利男爵邀請約翰上校和勇敢的獵象人夸特曼開始了尋找埋葬在黑暗地底的所羅門王寶藏的艱險歷程。他們橫穿渺無邊際的沙漠和濃廕庇日的原始森林,越過洶涌澎湃的激流險灘,翻越高聳入雲的峻嶺雪山,飽嘗沙漠的酷熱和冰雪嚴寒,在藏寶圖的指引下來到非洲一個原始的神祕國度庫庫安納。這裏有殘酷的人殉制度,有一個擁有一千個妻室的獨眼暴君特瓦拉,有像兀鷲一般醜惡詭詐老而不死的女巫加古爾,還有美麗聰慧的絕代佳人弗拉塔。在這片陌生而又險象環生的土地上三位尋寶英雄歷盡艱辛,終於在絕代佳人弗拉塔的幫助下在海底深處找到了珍藏這批價值連城寶藏的巨大的藏寶洞。然而在女巫加古爾的精心策劃下,一場滅頂之災正在悄悄逼近。

藏寶洞的洞門十分堅固且洞門緊閉,如果不知道開啓洞門的祕密是無法打開藏寶洞的洞門。在藏寶洞的洞門一側有一個奇怪的矩形密碼陣列。根據尋寶祕籍的記載,在密碼陣列每行的左側和每列的頂端都有一顆紅寶石按鈕。每個按鈕都可以向左或向右轉動。每向左轉動一次按鈕,相應的行或列中數字都增 1。每向右轉動一次按鈕,相應的行或列中數字都減 1。在矩形密碼陣列的若干特定位置鑲嵌着綠寶石。只有當所有綠寶石位置的數字與藏寶圖記載的密碼完全相同,緊閉的洞門就會自動緩緩打開。女巫加古爾早已得知開門的祕密。爲了阻止尋寶者打開洞門,女巫加古爾爲開門的密碼陣列設置了全 0 的初始狀態。試圖打開洞門的尋寶者如果不能迅速轉動按鈕使所有綠寶石位置的數字與藏寶圖記載的密碼完全相同,就會自動啓動藏寶洞玄妙的暗器機關,使尋寶者遭到滅頂攻擊而死於非命。

您能幫助三位尋寶英雄順利打開藏寶洞的洞門嗎?

編程任務:對於給定的密碼陣列,找到獲得正確密碼的紅寶石按鈕的轉動序列。


題解

題外話

困擾了我很久,主要是因爲對差分約束沒什麼印象,我還是肽蒻了QAQ

不會markdown求諒解QAQ


前置芝士

差分約束

專門解決給一些亂七八糟的不等式,再求某個點的最小/大值問題。
這裏假設大家都會。


正題

設第i行的操作使這一行的數增加了XiX_i,第j列的操作使這一列的數減少了YjY_j
那麼顯然對於第i行第j列綠寶石的密碼c,有XiX_i-YjY_j=c。
{XiYj<=cXiYj>=c 顯然可以轉化爲 \left \{ \begin{array}{ll} X_i-Y_j<=c\\ X_i-Y_j>=c \end{array} \right.
{XiYj<=cYjXi<=c 再化爲差分形式 \left \{ \begin{array}{ll} X_i-Y_j<=c\\ Y_j-X_i<=-c \end{array} \right.
XiX_iYjY_j的邊權爲c的邊和YjY_jXiX_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!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章