【CCF-201809-4】再买菜(100) 差分约束spfa Apare_xzc

【CCF-201809-4】再买菜 差分约束


这题卡了2天了,也算是把差分约束复习了一遍,加深理解

题面

Sample Input

8
2 2 1 3 4 9 10 13

Sample Output

2 2 2 1 6 5 16 10

参考博客:

https://blog.csdn.net/u014390156/article/details/82775736


推荐差分约束学习

夜深人静写算法(四)- 最短路和差分约束 - WhereIsHeroF…_CSDN博客

差分约束算法总结


分析

spfa 求差的最小值:把不等式都转化为>=的形式,建图之后求最长路。
因为整除的性质,我们可以根据题意得到如下的一组不等式
(1). b[1]*2 <= a1+a2    <= b[1]*2+1
(2). b[2]*3 <= a1+a2+a3 <= b[2]*3+2
(3). b[3]*3 <= a2+a3+a4 <= b[3]*3+2
(4). b[4]*3 <= a3+a4+a5 <= b[4]*3+2
(5). b[5]*3 <= a4+a5+a6 <= b[5]*3+2
_._._
(i). b[i]*3   <= a[i-1]+a[i]+a[i+1] <= b[i]*3+2
._._. 
(n-1).b[n-1]*3 <= a[n-2]+a[n-1]+a[n] <= b[n-1]*3+2
(n)  .b[n]*2   <= a[n-1]+a[n]        <= b[n] * 2+2
____________________________________________________
根据差分约束的性质,我们要得到字典序最小的菜价序列,
那么,我们要把不等式组转化为Xi-Xj>=d的形式,然后建立
 j->i 长度为d的单向边
____________________________________________________ 
由题意直接得出的式子都是ai+aj,我们需要引入一个辅助数
列:s[n] = a0+a1+a2+...+an (a0=0)
就是a[n]的前缀和,则:
s0 = a0
s1 = a0+a1
s2 = a0+a1+a2 
s3 = a0+a1+a2+a3
s4 = a0+a1+a2+a3+a4
s5 = a0+a1+a2+a3+a4+a5
...
sn = a0+a1+a2+a3+a4+...+an 
那么:
(1) a1+a2 = s2-s0
(2) a1+a2+a3 = s3-s0
(3) a2+a3+a4 = s4-s1
(4) a3+a4+a5 = s5-s2
......
(n-1) a[n-2]+a[n-1]+a[n] = s[n]-s[n-3]
(n)   a[n-1]+a[n] = s[n]-s[n-2]
______________________________________
--------------------------------------
所以,我们可以得到如下的式子:
(1)   b[1]*2 <= s2-s0 <= b[1]*2+1 
(2)   b[2]*3 <= s3-s0 <= b[2]*3+2
(3)   b[3]*3 <= s4-s1 <= b[3]*3+2
(4)   b[4]*3 <= s5-s2 <= b[4]*3+2
...
(i)   b[i]*3 <= s[i+1]-s[i-2] <= b[i]*3+2
...
(n-1) b[n-1]*3 <= s[n]-s[n-3] <= b[n-1]*3+2
(n)   b[n]*2   <= s[n]-s[n-2] <= b[n] * 2+1
---------------------------------------
_______________________________________
我们将上面n个式子转化为Xi>=xj+d的形式:
s2 >= s0 + (2*b1)       ===> X0->X2 : 2*b1
s0 >= s2 + (-2*b1-1)    ===> X2->X0 : -2*b1-1
--------------------
s3 >= s0 + (3*b2)      
s0 >= s3 + (-3*b2-2)
s4 >= s1 + (3*b3)
s1 >= s4 + (-3*b3-2)
......
s[i+1] >= s[i-2] + (3*b[i])    ===> X[i-2]->X[i+1]: 3*bi
s[i-2] >= s[i+1] + (-3*b[i]-2) ===> X[i+1]->X[i-2]: -3*bi-2
......
s[n]   >= s[n-3] + (3*b[n-1])
s[n-3] >= s[n]   + (-3*b[n-1]-2)
--------------------
s[n]   >= s[n-2] + (b[n]*2)   ===> X[n-2]->X[n]   : bn*2
s[n-2] >= s[n]   + (-2*b[n]-1)===> X[n]  ->X[n-2] : -2*bn-1
___________________________________________________________
然后,我们发现还有一组限制条件,就是每天的菜价都是正整数
即为:a1>=1, a2>=1, a3>=1,... an>=1
可得:s1-s0>=1    ===> s1>=s0+1 ===> X[0]->X[1]:1
      s2-s1>=1
	  ...
	  s[i]-s[i-1]>=1 ===> si>=s[i-1]+1 ===>X[i]->X[i-1]:1
	  ...
	  sn-s[n-1]>=1 
PS: 由于求最长路,而且图可能不连通,把dis[i]都初始化成0,然后都入队

这个人的代码很神奇,没有节点全入队,也100分,而我只push(st)就只有50分,之后再研究一下 <-

代码

#include <bits/stdc++.h>
using namespace std;
const int maxn =315;
const int maxm = 6000+5;
const int INF = 0x3f3f3f3f;
int b[maxn];
struct Node{
	int to,Next,d;
}node[maxm];
int head[maxn],tot;

void initEdge()
{
	tot = 0;
	memset(head,-1,sizeof(head));
}
void addedge(int from,int to,int d)
{
// printf("%d -> %d : %d\n",from,to,d);
	node[tot].d = d;
	node[tot].to = to;
	node[tot].Next = head[from];
	head[from] = tot++;
}
bool inQue[maxn]; //节点是否在队列中 
int dis[maxn];    //源点到节点的最短距离 
int num[maxn];    //节点入队的次数 
bool spfa(int n) //求最长路 
{
	int NN = sqrt(n);
	queue<int> Q;
// for(int i=0;i<=n;++i)
// inQue[i]=false,num[i]=0,dis[i]=0;
// Q.push(st);
// inQue[st] = true;
// num[st]++;
// dis[st] = 0;
	for(int i=0;i<=n;++i)
	{
		Q.push(i);
		inQue[i] = true;
		num[i] = 1;
		dis[i] = 0;
	}
	while(!Q.empty())
	{
		int u = Q.front();
		Q.pop();
		inQue[u] = false;
		for(int i=head[u];i!=-1;i=node[i].Next)
		{
			int v = node[i].to;
			int d = node[i].d;
			if(dis[u]+d>dis[v])
			{
				dis[v] = dis[u]+d;
				if(!inQue[v])
				{
					Q.push(v);
					inQue[v] = true;
					if(++num[i]>NN) return false;
				}
			}
		}
	}
	return true;
}
int main()
{
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;++i)
		scanf("%d",b+i);
	initEdge();
	addedge(2,0,-2*b[1]-1);  
	addedge(0,2,2*b[1]);
	for(int i=2;i<=n-1;++i)
		addedge(i-2,i+1,3*b[i]),       
		addedge(i+1,i-2,-3*b[i]-2);   	
	addedge(n-2,n,2*b[n]);    
	addedge(n,n-2,-2*b[n]-1);
	for(int i=1;i<=n;++i)
		addedge(i-1,i,1);
	for(int i=1;i<=n;++i)
		addedge(0,i,i);
	bool ok = spfa(n);
// cout<<(ok?"没有负环":"有负环")<<endl;
	printf("%d",dis[1]); 
	for(int i=2;i<=n;++i)
		printf(" %d",dis[i]-dis[i-1]);
	printf("\n");

	return 0;
}        
全部评论

相关推荐

杨柳哥:这不是普通人,那这个钱的是天才
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务