• 【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
    ",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("
    ");
    
    	return 0;
    }        
    
  • 相关阅读:
    linux上安装mysql
    linux 上nginx配置
    js人民币数字转大写
    pm2常用命令
    基于redis实现分布式锁
    Enterprise Architect 14破解版 安装包 安装教程
    一些有用的链接
    Linux安装Zookeeper
    根据朋友圈的网易云音乐分享找到人
    我的待做事项
  • 原文地址:https://www.cnblogs.com/Apare-xzc/p/12243631.html
Copyright © 2020-2023  润新知