CSP-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,则认为第一个序列的字典序小于第二个序列。
第一天,每个商店都自己定了一个正整数的价格。店主们希望自己的菜价和其他商店的一致,第二天,每一家商店都会根据他自己和相邻商店的价格调整自己的价格。具体的,每家商店都会将第二天的菜价设置为自己和相邻商店第一天菜价的平均值(用去尾法取整)。
注意,编号为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个正整数,依次表示每个商店第二天的菜价。
输出格式
输出一行,包含n个正整数,依次表示每个商店第一天的菜价。
样例输入
8
2 2 1 3 4 9 10 13
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的正整数。
请注意,以上都是给的第二天菜价的范围,第一天菜价可能会超过此范围。
对于60%的评测用例,2<=n<=20,第二天每个商店的菜价为不超过100的正整数;
对于所有评测用例,2<=n<=300,第二天每个商店的菜价为不超过100的正整数。
请注意,以上都是给的第二天菜价的范围,第一天菜价可能会超过此范围。
第一眼看过去就是dp,但是复杂度有点爆炸,然后以为dp不可解。
就试了试dp没想到竟然A了,前提是数据不是很极限否则A不了的。学正解的话还是去学差分约束吧,范围再大点也能解,dp只能应付小范围数据。(差分约束解在下面)
令f[i][j][k]表示 第一天:i位置的菜价为j,i+1位置的菜价为k
此时是否可以满足第二天:[i+1,n]位置的菜价约束,是的话f[i][j][k]=1否则f[i][j][k]=0
nt[i][j][k]就是记录下满足条件的最小的一个第i+2天的菜价l是多少,方便找路径输出。
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 bool f[310][310+5][310+5]; 5 int nt[310][310+5][310+5]; 6 int a[310],n,MAX=0; 7 int main(){ 8 cin>>n; 9 for(int i=1;i<=n;++i)cin>>a[i],MAX=max(MAX,a[i]*3+5); 10 for(int i=1;i<=MAX;++i){ 11 for(int j=1;j<=MAX;++j){ 12 if((i+j)/2==a[n]){ 13 f[n-1][i][j]=1; 14 nt[n-1][i][j]=0; 15 } 16 } 17 } 18 for(int i=n-1;i>=2;--i){ 19 for(int k=MAX;k>=1;--k){ 20 for(int l=MAX;l>=1;--l){ 21 if(f[i][k][l]){ 22 for(int j=1;j<=MAX;++j){ 23 if((j+k+l)/3==a[i]){ 24 f[i-1][j][k]=1; 25 nt[i-1][j][k]=l; 26 } 27 } 28 } 29 } 30 } 31 } 32 bool con=1; 33 for(int i=1;con&&i<=MAX;++i){ 34 for(int j=1;con&&j<=MAX;++j){ 35 if(f[1][i][j] && (i+j)/2==a[1]){ 36 int l=i,r=j; 37 cout<<l; 38 for(int k=2;k<=n;++k){ 39 cout<<' '<<r; 40 int tmp=nt[k-1][l][r]; 41 l=r; 42 r=tmp; 43 } 44 con=0; 45 } 46 } 47 }cout<<endl; 48 return 0; 49 }
下面是差分约束的代码:
利用前缀和S(n)-S(n-2)>=K 来代替a[i-1]+a[i]+a[i+1]>=K然后建边,注意每个S(i)-S(0)>=1这个隐含的条件。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define LL long long 4 #define inf 0x3f3f3f3f 5 #define pii pair<int,int> 6 #define mp make_pair 7 8 const int maxn=310; 9 vector<pii>g[maxn]; 10 int n; 11 int a[maxn]; 12 bool inq[maxn]; 13 int d[maxn]; 14 bool spfa(){ 15 queue<int>q; 16 memset(d,-inf,sizeof(d)); 17 d[0]=0; 18 inq[0]=1; 19 q.push(0); 20 while(!q.empty()){ 21 int u=q.front();q.pop();inq[u]=0; 22 for(auto e:g[u]){ 23 int v=e.first,w=e.second; 24 if(d[v]<d[u]+w){ 25 d[v]=d[u]+w; 26 if(!inq[v]){ 27 inq[v]=1; 28 q.push(v); 29 } 30 } 31 } 32 }return 1; 33 } 34 35 int main() 36 { 37 cin>>n; 38 for(int i=1;i<=n;++i)cin>>a[i]; 39 for(int i=1;i<=n;++i){ 40 if(i==1){ 41 g[0].push_back(mp(2,2*a[1])); 42 g[2].push_back(mp(0,-2*a[1]-1)); 43 }else if(i==n){ 44 g[n-2].push_back(mp(n,2*a[n])); 45 g[n].push_back(mp(n-2,-2*a[n]-1)); 46 }else{ 47 g[i-2].push_back(mp(i+1,3*a[i])); 48 g[i+1].push_back(mp(i-2,-3*a[i]-2)); 49 } 50 } 51 for(int i=0;i<=n-1;++i) g[i].push_back(mp(i+1,1)); 52 spfa(); 53 for(int i=1;i<=n;++i)cout<<d[i]-d[i-1]<<' ';cout<<'\n'; 54 return 0; 55 }