201809-4 再卖菜

问题描述

  在一条街上有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的正整数。
  请注意,以上都是给的第二天菜价的范围,第一天菜价可能会超过此范围。

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
#define rep(i,a,b) for(int i=a;i<b;++i)

const int N=310;
const int INF=0x3f3f3f3f;

int arr[N],sum[N];

struct Edge{
	int v,w;
	Edge(int _v=0,int _w=0){
		v=_v,w=_w;
	}
};
vector<Edge> edge[N];
void addEdge(int u,int v,int w){
	//printf("%d %d %d\n",u,v,w);
	
	edge[u].push_back(Edge(v,w));
}

int dis[N],in[N];
struct Node{
	int x,dis;
	Node(int _x=0,int _dis=0){
		x=_x,dis=_dis;
	}
};
queue<Node>q;
void spfa(){
	in[0]=1;
	q.push(Node(0,0));
	while(!q.empty()){
		Node now=q.front();
		q.pop();
		
	//	printf("now.x:%d dis:%d\n",now.x,dis[now.x]);
		in[now.x]=0;
		for(int i=0;i<edge[now.x].size();i++){
			Edge e=edge[now.x][i];
			if(dis[e.v]<dis[now.x]+e.w){
				dis[e.v]=dis[now.x]+e.w;
				if(in[e.v]==0){
					in[e.v]=1;
					q.push(Node(e.v,dis[e.v])); 
				}
			}
		}
	}
}

int main(){
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&arr[i]);
		sum[i]=sum[i-1]+arr[i];
	}
	
	addEdge(0,1,1);
	addEdge(n-1,n,1);
	addEdge(0,2,2*arr[1]);
	addEdge(2,0,-(2*(arr[1]+1)-1));
	addEdge(n-2,n,2*arr[n]);
	addEdge(n,n-2,-(2*(arr[n]+1)-1));
	
	for(int i=2;i<=n-1;i++){
		addEdge(i-1,i,1);
		addEdge(i-2,i+1,3*arr[i]);
		addEdge(i+1,i-2,-(3*(arr[i]+1)-1));
	}
	
	spfa();

	for(int i=1;i<=n;i++){
		if(i==1)printf("%d",dis[i]-dis[i-1]);
		else printf(" %d",dis[i]-dis[i-1]);
	}
	printf("\n");
	
	return 0;
} 

 

差分约束

 

最短路:

考虑最短距离: 

         假设 1 和 2 之间有一条边 \LARGE e_{12},  我们可以得出  \large dis[2]-dis[1]<=e_{12},

建边的时候,就可以参照来做了。

但是!!!   得到的答案的字典序 是最大的!!, 不是说最短路就是每个值都是最小的,字典序最小。

得到的答案是,约束的上边界!!!

    还是考虑一个简单地例子,\large dis[2]-dis[1]<=2(假设dis[1]=0), 说明 \large dis[2]<=2,  我们得到的是

上边界。

 

 

最长路

形式 :   \large d(v) + \alpha <= d(u) + \beta

\large \alpha 和\beta\large \beta 是权值的位置,首先 \large d(u)\large d(v) 这两个未知数,前面保证没有负号。

边权的形式,我们考虑  \large W(u,v),

(1)如果求 未知数符合所有条件的最小值, 就是用最长路, 考虑  \large d(v)>=d(u)+w(u,v),

所以那个  权值就在  小于号那边

(2)如果求 未知数符合所有条件的最大值,就是用最短路, 考虑 \large d(v)<=d(u)+w(u,v)

那个 权值 就在大于号那边

 

 

 

这道题 求 字典序最小,可以理解为求 最小值, 所以用最长路。

 

注意:

1.列出所有的条件, 包括 每个点都要大于等于某些数值(条件不足的话,图也不连通

2.标一个 超级源点 S0

3.而且每个不等式只能是 两个未知数。 如果多个,像这道题,我们需要引出其他的变量 sum,变成差值来做

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值