2019牛客暑期多校訓練營(第三場)----I-Median

首先發出題目鏈接:
鏈接:https://ac.nowcoder.com/acm/contest/883/I
來源:牛客網
涉及:思維,動態規劃

點擊這裏回到2019牛客暑期多校訓練營解題—目錄貼


題目如下:
在這裏插入圖片描述
在這裏插入圖片描述
可以看出 a[1]a[1] 是可以取 a[1],a[2],a[3]a[1], a[2],a[3] 的中位數,a[n]a[n] 可以取 a[n2],a[n1],a[n]a[n-2],a[n-1],a[n] 的中位數,這樣子是不會影響中位數的取值。

首先可以證明 aa 數組內的數可以都是由 bb 數組內的數構成,爲什麼說是可以而不是一定?我們假設 aa 數組內存在某一個數 bb 數組內沒有,假設這個數爲 a[i]a[i],如下圖所示
在這裏插入圖片描述
其中 a[i1],a[i],a[i+1]a[i-1],a[i],a[i+1] 的中位數爲 b[i+1]b[i+1] ;
   a[i],a[i+1],a[i+2]a[i],a[i+1],a[i+2] 的中位數爲 b[i+2]b[i+2] ;
   a[i+1],a[i+2],a[i+3]a[i+1],a[i+2],a[i+3] 的中位數爲 b[i+3]b[i+3] ;

也就是說 a[i]a[i] 同時影響着 b[i],b[i+1],b[i+2]b[i],b[i+1],b[i+2] ,那麼 a[i]a[i] 的可能和 b[i],b[i+1],b[i+2]b[i],b[i+1],b[i+2] 中的某個值相同。

但是現在假設 a[i]a[i]b[i],b[i+1],b[i+2]b[i],b[i+1],b[i+2] 都不同,那麼 a[i]a[i] 只可能是大於等於 b[i],b[i+1],b[i+2]b[i],b[i+1],b[i+2] 的最大值,或者小於等於 b[i],b[i+1],b[i+2]b[i],b[i+1],b[i+2] 的最小值。假設 a[i]a[i] 大於等於 b[i],b[i+1],b[i+2]b[i],b[i+1],b[i+2] 的最大值,那麼 a[i]a[i] 完全可以取 b[i],b[i+1],b[i+2]b[i],b[i+1],b[i+2] 中的最大值,這樣的話與假設相反,也就是說 aa 數組內的數完全可以都是由 bb 數組內的數構成。


知道 aa 數組內的數是由 bb 數組內的數構成。

那麼可以確定每一個 a[i]a[i] 的取值可以爲 b[i],b[i+1],b[i+2]b[i],b[i+1],b[i+2] 中的一個,用一個 can[i][3]can[i][3] 數組來存每一個 a[i]a[i] 可能取的三個值。

由於 a[1]a[1] 是可以取 a[1],a[2],a[3]a[1], a[2],a[3] 的中位數,a[n]a[n] 可以取 a[n2],a[n1],a[n]a[n-2],a[n-1],a[n] 的中位數,可以按下面的方式處理

for(int i = 3; i <= n; i++){
	scanf("%d", &b[i]);//b[i] 表示 a[i-2],a[i-1],a[i] 的中位數,a[i] 影響着 b[i],b[i+1],b[i+2]
}
b[1] = b[2] = b[3];
b[n+2] = b[n+1] = b[n];
for(int i = 1; i <= n; i++){
	can[i][1] = b[i];
	can[i][2] = b[i+1];
	can[i][3] = b[i+2];
}

這樣子很明顯 can[1][1]=can[1][2]=can[1][3]can[1][1]=can[1][2]=can[1][3]can[n][1]=can[n][2]=can[n][3]can[n][1]=can[n][2]=can[n][3],表示 a[1]a[1]a[n]a[n] 的取值已經確定了。

有了這種關係,就可以開始dp了。
dp[i][j][k]=1dp[i][j][k]=1 表示 a[i]a[i]can[i][j]can[i][j] , a[i1]a[i-1]can[i1][k]can[i-1][k] 可以成立,否則表示不成立。很明顯 1in,  1j,k31\le i\le n,\ \ 1\le j,k\le 3

探討 dp[i][j][k]dp[i][j][k] 成立的條件:
1.先討論滿足 a[i1]a[i-1]can[i1][k]can[i-1][k] 成立的條件,那肯定得存在 l(1l3)l(1\le l\le3),使得 dp[i1][k][l]dp[i-1][k][l]成立,即 a[i1]a[i-1]can[i1][k]can[i-1][k]a[i2]a[i-2]can[i2][l]can[i-2][l] 成立。

2.在討論滿足 a[i]a[i]can[i][j]can[i][j] 成立的條件,此時 a[i1]a[i-1]can[i1][k]can[i-1][k] 已經成立,那麼只要滿足 mid(can[i2][l],can[i1][k],can[i][j])==b[i]mid(can[i-2][l], can[i-1][k], can[i][j]) == b[i]
can[i2][l],can[i1][k],can[i][j]can[i-2][l], can[i-1][k], can[i][j] 的中位數爲 b[i]b[i]

那麼最後只要存在任意的 j,kj,k ,使得 dp[n][j][k]=1dp[n][j][k]=1 就說明存在這樣的 aa 序列。

int now, last;//表示dp[n][now][last]=1
bool isfind = false;//判斷是否找到這樣的dp[n][j][k]成立
for(int i = 1; i <= 3; i++){
	for(int j = 1; j <= 3 && !isfind; j++){
		if(dp[n][i][j]){
			isfind = true;
			now = i;
			last = j;
		}
	}
}
if(!isfind){
	cout << "-1" << endl;
	continue;
}

同時,還需要一個數組 pre[i][j][k]pre[i][j][k]pre[i][j][k]=lpre[i][j][k] = l 表示當 pre[i][j][k]pre[i][j][k] 成立時,dp[i1][k][l]dp[i-1][k][l] 也成立,這個數組用來回溯結果,從而得到 aa 序列的值。

for(int i = 3; i <= n; i++){//dp全過程
	for(int j = 1; j <= 3; j++){
		for(int k = 1; k <= 3; k++){
			for(int l = 1; l <= 3; l++){
				if(dp[i-1][k][l] && mid(can[i-2][l], can[i-1][k], can[i][j]) == b[i]){
					dp[i][j][k] = 1;
					pre[i][j][k] = l;
				}
			}
		}
	}
}

回溯得到 aa 序列的過程(代碼中顯示的是ans序列)

for(int i = n; i >= 2; i--){
	ans[i] = can[i][now];//dp[i][now][last]成立表示ans[i]=can[i][now],ans[i-1]=can[i-1][last]
	int temp = pre[i][now][last];//回溯得到是由dp[i-1][last][temp]轉移而來的
	now = last;
	last = temp;
}
ans[1] = can[1][1];//ans[1]單獨賦值

代碼如下:

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn = 1e5+5;
int t, n;
int ans[maxn], b[maxn];
int can[maxn][5];
int dp[maxn][5][5];
int pre[maxn][5][5];
int mid(int a, int b, int c){//求三個數中位數的函數
	if(a > b)	swap(a, b);
	if(b > c)	swap(b, c);
	if(a > b)	swap(a, b);
	return b;
}
int main(){
	cin >> t;
	while(t--){
		scanf("%d", &n);
		for(int i = 3; i <= n; i++){
			scanf("%d", &b[i]);//b[i] 表示 a[i-2],a[i-1],a[i] 的中位數,a[i] 影響着 b[i],b[i+1],b[i+2]
		}
		b[1] = b[2] = b[3];
		b[n+2] = b[n+1] = b[n];
		for(int i = 1; i <= n; i++){//求出can數組,得到每個位置可能的值
			can[i][1] = b[i];
			can[i][2] = b[i+1];
			can[i][3] = b[i+2];
		}
		for(int i = 1; i <= n; i++){
			for(int j = 1; j <= 3; j++){
				for(int k = 1; k <= 3; k++){
					pre[i][j][k] = dp[i][j][k] = 0;//初始化pre和dp數組
				}
			}
		}
		for(int i = 1; i <= 3; i++){
			for(int j = 1; j <= 3; j++){
				dp[2][i][j] = 1;//dp數組初始化
				//假設一開始ans[2]位置可以取can[2][1-3]的任何值
			}
		}
		for(int i = 3; i <= n; i++){//開始dp
			for(int j = 1; j <= 3; j++){
				for(int k = 1; k <= 3; k++){
					for(int l = 1; l <= 3; l++){
						if(dp[i-1][k][l] && mid(can[i-2][l], can[i-1][k], can[i][j]) == b[i]){
							//dp[i][j][k]成立的三個條件
							dp[i][j][k] = 1;
							pre[i][j][k] = l;
						}
					}
				}
			}
		}
		int now, last;//表示dp[n][now][last]=1
		bool isfind = false;//判斷是否找到這樣的dp[n][j][k]成立
		for(int i = 1; i <= 3; i++){
			for(int j = 1; j <= 3 && !isfind; j++){
				if(dp[n][i][j]){
					isfind = true;
					now = i;
					last = j;
				}
			}
		}
		if(!isfind){//找不到輸出-1
			cout << "-1" << endl;
			continue;
		}
		for(int i = n; i >= 2; i--){
			ans[i] = can[i][now];//dp[i][now][last]成立表示ans[i]=can[i][now],ans[i-1]=can[i-1][last]
			int temp = pre[i][now][last];//回溯得到是由dp[i-1][last][temp]轉移而來的
			now = last;
			last = temp;
		}
		ans[1] = can[1][1];//ans[1]單獨賦值
		for(int i = 1; i <= n; i++){
			cout << ans[i] << " ";
		}
		cout << endl;
	}
	return 0;
} 
發佈了49 篇原創文章 · 獲贊 91 · 訪問量 3984
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章