問題描述
在一條街上有n個賣菜的商店,按1至n的順序排成一排,這些商店都賣一種蔬菜。
第一天,每個商店都自己定了一個正整數的價格。店主們希望自己的菜價和其他商店的一致,第二天,每一家商店都會根據他自己和相鄰商店的價格調整自己的價格。具體的,每家商店都會將第二天的菜價設置爲自己和相鄰商店第一天菜價的平均值(用去尾法取整)。
注意,編號爲1的商店只有一個相鄰的商店2,編號爲n的商店只有一個相鄰的商店n-1,其他編號爲i的商店有兩個相鄰的商店i-1和i+1。
給定第二天各個商店的菜價,可能存在不同的符合要求的第一天的菜價,請找到符合要求的第一天菜價中字典序最小的一種。
字典序大小的定義:對於兩個不同的價格序列(a1, a2, ..., an)和(b1, b2, b3, ..., bn),若存在i (i>=1), 使得ai<bi,且對於所有j<i,aj=bj,則認爲第一個序列的字典序小於第二個序列。
輸入格式
輸入的第一行包含一個整數n,表示商店的數量。
第二行包含n個正整數,依次表示每個商店第二天的菜價。
輸出格式
輸出一行,包含n個正整數,依次表示每個商店第一天的菜價。
樣例輸入
8
2 2 1 3 4 9 10 13
樣例輸出
2 2 2 1 6 5 16 10
數據規模和約定
對於30%的評測用例,2<=n<=5,第二天每個商店的菜價爲不超過10的正整數;
對於60%的評測用例,2<=n<=20,第二天每個商店的菜價爲不超過100的正整數;
對於所有評測用例,2<=n<=300,第二天每個商店的菜價爲不超過100的正整數。
請注意,以上都是給的第二天菜價的範圍,第一天菜價可能會超過此範圍。
a1*2 <= x1+x2 <= a1*2+1 a2*3 <= x1+x2+x3 <= a2*3+2
a3*3 <= x2+x3+x4 <= a3*3+2 a4*3 <= x3+x4+x5 <= a4*3+2
...... an*2 <= xn+x(n-1) <= an*2+1
設 sn=x0+x1+x2+...+xn
則 s2-s0 >= 2*a1 s0-s2 >= -(2*a1+1) s3-s0 >= 3*a2 s0-s3 >= -(3*a2+2)
...... sn-s(n-2) >= an*2 s(n-2) - sn >= -(an*2 +1)
s1-s0 >= 1 s2-s1 >= 1 ....... sn-s(n-1) >= 1
因此可利用差分約束來建立圖,只要求得 0 到 n 點的最長路,便可以得到字典序最小解。
spfa中的head數組是用來模擬鄰接表的,鬆弛操作可以看作是對 u 點的一次廣度優先遍歷,將所有與之相連的點都進行鬆弛,將所有鬆弛過的點加入隊列,進行下一步鬆弛運算。
#include<stdio.h>
#include<queue>
#include<memory.h>
using namespace std;
#define N 310 //最大點數
struct{
int next,end,wight;
}E[2019];
bool vis[N]; //標記數組 用於記錄當前點是否遍歷過
int dis[N]; //存放當前點目前的最短距離
int a[N]; //存放第二天各商店菜價
int n; //頂點數
int total; //邊數
int start_vertex; //起始點
int head[N]; //模擬鄰接表所用
void add(int be,int en,int wi){
E[total].next=head[be]; //該鄰接表的前一條邊的編號
E[total].end=en; //當前邊的出度點
E[total].wight=wi; //當前邊的權重
head[be]=total++; //當前邊的編號
}
void SPFA(){
//memset(vis,0,sizeof(vis));
queue<int> q; //SPFA所需的優化隊列
for(int i=0;i<=n;i++){ //將所有的點都入隊,因爲無法確定該圖是否連通
dis[i]=0;
q.push(i);
vis[i]=true;
}
while(!q.empty()){
int u=q.front();
q.pop();
vis[u]=false;
for(int i=head[u];i!=-1;i=E[i].next){ //遍歷所有與 U 點相連的邊,對其出度點進行鬆弛操作
int end=E[i].end;
if(dis[end]<E[i].wight+dis[u]){
dis[end]=E[i].wight+dis[u];
if(!vis[end]){
q.push(end);
vis[end]=true;
}
}
}
}
dis[start_vertex]=0;
for(int i=1;i<=n;i++){
printf("%d ",dis[i]-dis[i-1]);
}
}
int main(){
scanf("%d",&n); //店鋪數量
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
memset(head,-1,sizeof(head));
total=0;
start_vertex=0;
add(0,2,(a[1]*2)); //sj-si≥k,i到j建立一條權值爲k的單向邊
add(2,0,-(a[1]*2+1));
add(n-2,n,(a[n]*2));
add(n,n-2,-(a[n]*2+1));
for(int i=2;i<=n-1;i++)
{
add(i-2,i+1,(a[i]*3));
add(i+1,i-2,-(a[i]*3+2));
}
for(int i=1;i<=n;i++)
add(i-1,i,1);
SPFA();
printf("\n");
return 0;
}