CCF 差分约束--201809再卖菜

问题描述

  在一条街上有n个卖菜的商店,按1至n的顺序排成一排,这些商店都卖一种蔬菜。   第一天,每个商店都自己定了一个正整数的价格。店主们希望自己的菜价和其他商店的一致,第二天,每一家商店都会根据他自己和相邻商店的价格调整自己的价格。具体的,每家商店都会将第二天的菜价设置为自己和相邻商店第一天菜价的平均值(用去尾法取整)。   注意,编号为1的商店只有一个相邻的商店2,编号为n的商店只有一个相邻的商店n-1,其他编号为i的商店有两个相邻的商店i-1和i+1。   给定第二天各个商店的菜价,可能存在不同的符合要求的第一天的菜价,请找到符合要求的第一天菜价中字典序最小的一种。   字典序大小的定义:对于两个不同的价格序列(a 1, a 2, ..., a n)和(b 1, b 2, b 3, ..., b n),若存在i (i>=1), 使得a i<b i,且对于所有j<i,a j=b j,则认为第一个序列的字典序小于第二个序列。

输入格式

  输入的第一行包含一个整数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的正整数。   请注意,以上都是给的第二天菜价的范围,第一天菜价可能会超过此范围。

解析:

  由于是去尾法取整,所以这题的每组相邻的商店菜价总和是由区间限制的,可以利用差分约束来做。差分约束有一般有两种求解方式,求最大值,求最小值。这里求字典序最小即要求最小值,可以利用spfa()求最长路径不理解的戳这里)。建图还是采用我个人最喜欢的链式向前星不懂戳这里)。

  PS:链接里的大概意思是,求最长路的松弛操作是if (dist[end]<dist[sta]+边权值){ dist[end]=dist[sta]+权值; },求出的dist[end]的解是满足约束条件(>=dist[sta]+权值)中最小的(因为取的是等号,还可能存在比这个更大的解)。求最大值的原理类似。

 1 #include <iostream>
 2 #include <queue>
 3 #include <cstring>
 4 using namespace std;
 5 struct Edge{
 6     int to;
 7     int next;//与当前边起点一样的另一条边的位置 
 8     int v;
 9 }edge[2006]; //链式向前星:每个节点存一条边; 
10 int n=0,cur=0; //cur当前已有边的个数 
11 int a[305],dist[306],vis[305],head[305],inq[305];
12 //head[i]以i为起点的边最大的编号 
13 void addedge(int from,int to,int w)
14 {
15     edge[cur].next=head[from];
16     edge[cur].to=to;
17     edge[cur].v=w;//路径权值 
18     head[from]=cur++;//当前节点变为头结点 
19 }
20 void spfa()//求最长路 
21 {
22     queue<int> q;
23     for(int i=0;i<n+1;++i)
24     {
25         q.push(i);
26         vis[i]=1;
27         dist[i]=0;
28         inq[i]=1;
29     }
30     while(!q.empty())
31     {
32         int x=q.front();
33         q.pop();
34         inq[x]++;
35         vis[x]=0;
36         if(inq[x]>n){//访问某一节点过多,存在正环,无解 
37             cout<<"no answer"<<endl;
38             return ;
39         }
40         for(int i=head[x];i!=-1;i=edge[i].next)//遍历与该节点相连的各边 
41         {
42             int nx=edge[i].to;
43             if(dist[nx]<dist[x]+edge[i].v)
44             {
45                 dist[nx]=dist[x]+edge[i].v;
46                 if(!vis[nx]){
47                     vis[nx]=1;
48                     q.push(nx);
49                 }
50             }
51         }
52     }
53     return ;
54 }
55 int main()
56 {    //解除cin cout 的绑定,提高输入输出效率;这个可以当模板记住,当然直接用scanf和print也可以。 
57     ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); 
58     memset(head,-1,sizeof(head));//head    全部初始为-1 
59     cin>>n;
60     for(int i=1;i<n+1;++i)
61         cin>> a[i];//第二天的菜价
62     for(int i=0;i<n-2;++i)
63     {
64         addedge(i+3,i,-(a[i+2]*3+2));//从第一个三个相邻开始,直到最后一个三个三个相邻 
65         addedge(i,i+3,a[i+2]*3);    
66     } 
67     //首末两个相邻,特殊处理 
68     addedge(2,0,-(a[1]*2+1));
69     addedge(0,2,a[1]*2); //首两个 
70     addedge(n,n-2,-(a[n]*2+1));
71     addedge(n-2,n,a[n]*2); //结尾两个
72     for(int i=1;i<n+1;++i)
73     {
74         addedge(i-1,i,1); //每个菜价都要大于1 
75     } 
76     spfa();
77     a[1]=dist[1];
78     for(int i=2;i<n+1;++i)
79         a[i]=dist[i]-dist[i-1];
80     cout<<a[1];
81     for(int i=2;i<n+1;++i)
82         cout<<' '<<a[i];
83     cout<<endl;
84     return 0;
85 } 

 (`・ω・´)ゞ敬礼っ

猜你喜欢

转载自www.cnblogs.com/GorgeousBankarian/p/10403920.html