首先發出題目鏈接:
鏈接:https://ac.nowcoder.com/acm/contest/883/I
來源:牛客網
涉及:思維,動態規劃
點擊這裏回到2019牛客暑期多校訓練營解題—目錄貼
題目如下:
可以看出 是可以取 的中位數, 可以取 的中位數,這樣子是不會影響中位數的取值。
首先可以證明 數組內的數可以都是由 數組內的數構成,爲什麼說是可以而不是一定?我們假設 數組內存在某一個數 數組內沒有,假設這個數爲 ,如下圖所示
其中 的中位數爲 ;
的中位數爲 ;
的中位數爲 ;
也就是說 同時影響着 ,那麼 的可能和 中的某個值相同。
但是現在假設 與 都不同,那麼 只可能是大於等於 的最大值,或者小於等於 的最小值。假設 大於等於 的最大值,那麼 完全可以取 中的最大值,這樣的話與假設相反,也就是說 數組內的數完全可以都是由 數組內的數構成。
知道 數組內的數是由 數組內的數構成。
那麼可以確定每一個 的取值可以爲 中的一個,用一個 數組來存每一個 可能取的三個值。
由於 是可以取 的中位數, 可以取 的中位數,可以按下面的方式處理
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];
}
這樣子很明顯 ,,表示 和 的取值已經確定了。
有了這種關係,就可以開始dp了。
設 表示 取 , 取 可以成立,否則表示不成立。很明顯
探討 成立的條件:
1.先討論滿足 取 成立的條件,那肯定得存在 ,使得 成立,即 取 , 取 成立。
2.在討論滿足 取 成立的條件,此時 取 已經成立,那麼只要滿足
即 的中位數爲
那麼最後只要存在任意的 ,使得 就說明存在這樣的 序列。
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;
}
同時,還需要一個數組 , 表示當 成立時, 也成立,這個數組用來回溯結果,從而得到 序列的值。
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;
}
}
}
}
}
回溯得到 序列的過程(代碼中顯示的是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;
}