题意:
问题描述
在一条街上有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的正整数。
请注意,以上都是给的第二天菜价的范围,第一天菜价可能会超过此范围。
分析及思路:
这道题是非常经典的最短路解差分约束问题,然而考试的时候发了草稿纸却没有拿笔,没看出来一开始还以为考的dp,索性直接暴力dfs一波,然而dfs也写搓了,只有30分,回来拿笔一列,我擦,居然是差分约束。
先讲讲差分约束为什么可以用最短路求:
首先,只有二元一次不等式建立的差分约束才能用最短路求解,因为图中的两个点是一一对应建立关系的。
举个例子,对于xj-xi≤k,这样的不等式非常像最短路的松弛操作,假设我们有两个点,i和j,在最短路中会有转移方程if(dist[j]-dist[i]>k),dist[j]=dist[i]+k;,看出来了吧,当前i到j有一条边权值为k,这是相当于对j更新,原来的dist[j]不满足约束条件,也就是dist[j]-dist[i]>k,更新后一定满足约束条件,更新后的dist[j]就满足dist[j]-dist[i]≤k,dist[i]就相当xi,也就是xj-xi≤k!而且这个更新后得到的序列是字典序最大的,为什么?很简单,因为当dist[j]-dist[i]>k时让dist[j]-dist[i]≤k,dist[j]可以更新到dist[i]+k-n,n可以取任意正整数都满足条件,但这里的更新就相当于n取零,使得dist[j]有最大值。
然而这道题要求字典序最小,怎么办呢?很简单,我们把所有的不等式都变成xi-xj≥k,反过来求最长路即可,跟最短路的原理是一模一样的,这里不多论述关于差分约束的讲解,网上有大量教程。
接下来讲如何建图的问题:
首先这里的不等式是这样的:
假设n=5,则有:
- 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
- a5* 2≤ x4+x5 ≤ a5* 2+1
- x1≥1
- x2≥1
- x3≥1
- x4≥1
- x5≥1
xi为第一天第i家店的售价,ai为第二天第i家点的售价。
不难发现存在3元一次不等式怎么办呢?我们可以引入中间变量si=x0+x1+…+xi,这里的x0等于0;
即可将上面的第一个不等式转化成: a1* 2≤ s2-s0 ,a1* 2+1≤s0-s2.其它的也一样,不一一列举,全部转化成大于等于的形式,sj-si≥k,i到j建立一条权值为k的单向边,然后spfa跑最长路即可,这里的spfa与原来的spfa有些区别,在spfa之前记得将所有点加入队列,并且dist置0,因为差分约束系统不保证图是连通的,所以要先建立一个虚拟节点,这个虚拟节点为源点,向所有边连接一条权值为0的有向边,以上的操作便是人工省略了虚拟源点的步骤。
有关于差分约束的更多问题可以上网找博客看。
提交官网结果:
AC代码:
#include<iostream>
#include<queue>
#include<cstring>
#define rep(i,x,n) for(int i=x;i<n;i++)
using namespace std;
//head
struct Edge{int to,next,v;}edge[2006];
int n,cur=0,a[306],head[306],dist[306],inq[306],vis[306];
void addedge(int x,int y,int v){edge[cur].to=y;edge[cur].next=head[x];edge[cur].v=v;head[x]=cur++;}
void spfa()
{
queue<int>qq;
rep(i,0,n+1)
{qq.push(i);vis[i]=1;dist[i]=0;inq[i]=1;}
while(!qq.empty())
{
int x=qq.front();
qq.pop();
inq[x]++;
vis[x]=0;
if(inq[x]>n){cout<<"noanswer"<<endl;return ;}
for(int i=head[x];i!=-1;i=edge[i].next)
{
int nx=edge[i].to;
if(dist[nx]<dist[x]+edge[i].v)
{
dist[nx]=dist[x]+edge[i].v;
if(!vis[nx])
{
vis[nx]=1;
qq.push(nx);
}
}
}
}
return ;
}
int main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
memset(head,-1,sizeof(head));
cin>>n;
rep(i,1,n+1)cin>>a[i];
rep(i,0,n-2){addedge(i+3,i,-(a[i+2]*3+2));addedge(i,i+3,a[i+2]*3);}
addedge(2,0,-(a[1]*2+1));addedge(0,2,a[1]*2); //对开始两个单独处理
addedge(n,n-2,-(a[n]*2+1));addedge(n-2,n,a[n]*2); //对结尾两个单独处理
rep(i,1,n+1)addedge(i-1,i,1); //每个数都要大于等于1
spfa();
a[1]=dist[1];
rep(i,2,n+1)a[i]=dist[i]-dist[i-1];
cout<<a[1];rep(i,2,n+1)cout<<' '<<a[i];
return 0;
}