问题描述
在一条街上有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;
}