再卖菜(差分约束与spfa)

试题编号: 201809-4
试题名称: 再卖菜
时间限制: 1.0s
内存限制: 256.0MB
问题描述:
问题描述
  在一条街上有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的正整数。
  请注意,以上都是给的第二天菜价的范围,第一天菜价可能会超过此范围。

分析
第一天第i个超市卖菜为x[i] (设i不为两端),第二天第i个超市卖菜为y[i] ,可知
(x[i-1]+x[i]+x[i+1])/2=y[i];记录x数组的前缀和为sum数组,则k可知下面等式
(sum[i+1]-sum[i-2])/3=y[i]; 向下取整操作可整理为下面的两个不等式
sum[i+1]-sum[i-2]>=3* y[i]
sum[i+1]-sum[i-2]<=3*y[i]+2
并且由题目介绍 第二天每个商店的菜价为不超过100的正整数可得到下面不等式
sum[i]-sum[i-1]>=1

存在差分约束,将其转换为图论问题,因为求最小值,转换成为求最长路
下面将不等式进行整理
sum[i+1]-sum[i-2]>=3* y[i] 从i-2到i+1一条长度为3* y[i]的边
sum[i+1]-sum[i-2]<=3* y[i]+2 => sum[i-2]-sum[i+1]>=-(3*y[i]+2)
sum[i]-sum[i-1]>=1 从i-1到i一条长度为1的边

下面是100分代码,其中d数组就是前缀和数组

#include<iostream>
#include<cstring>
#include<queue>

using namespace std;

int n;
const int N = 310;
int arr[N];
vector<pair<int, int>>g[N];
int d[N];
bool tg[N];

void spfa() {
    queue<int>q;
    memset(d, -0x3f, sizeof d);
    d[0] = 0;
    q.push(0);
    tg[0] = true;
    while (q.size()) {
        int ver = q.front();
        q.pop();
        tg[ver] = false;
        for (pair<int, int>p : g[ver]) {
            if (d[p.first] < d[ver] + p.second) {
                d[p.first] = d[ver] + p.second;
                if (!tg[p.first])q.push(p.first), tg[p.first] = true;
            }
        }
    }
}

int main() {
    cin >> n;
    for (int i = 1; i <= n; i++)cin >> arr[i];
    for (int i = 2; i < n; i++) {
        g[i - 2].push_back({ i + 1,3 * arr[i] });
        g[i + 1].push_back({ i - 2,-(3 * arr[i] + 2) });
    }
    g[0].push_back({ 2,2 * arr[1] });
    g[2].push_back({ 0,-(2 * arr[1] + 1) });
    g[n - 2].push_back({ n,2 * arr[n] });
    g[n].push_back({ n - 2,-(2 * arr[n] + 1) });
    for (int i = 1; i <= n; i++) g[i - 1].push_back({ i,1 });
    spfa();
    for (int i = 1; i <= n; i++)cout << d[i] - d[i - 1] << ' ';
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值