DP on the convex hull, WQS binary optimization

Foreword

Test bombed CSP, adjust the attitude adjustment. . .

Then school this point.

Feeling relatively simple learning algorithms really physical and mental pleasure.

references

A problem solution to a problem

Title set: https://www.cnblogs.com/HocRiser/p/9834069.html

Inspired on the whole point to proveAlthough I did not understandhttps://www.cnblogs.com/CreeperLKF/p/9045491.html

Limits on the difference between the minimum spanning tree of the WQS dichotomy: https://www.luogu.com.cn/blog/EndSaH/solution-cf125e

Example demonstrates

example

Topic Link

Chinese meaning of the questions, see for yourself. . .

Starting from explanations to explain

If you have no idea, I can responsibly tell you.

I did not just start thinking, even a prerequisite solution to a problem is I need to have proved the correctness of the practice after solution to a problem before launching a prerequisite.

I used the Kruskal approach.

We think about, we want the spanning tree contain a certain amount of white border, then they would change the white side position spanning tree, then we can add a weight to the side of each \ (k \) (can be positive , can be negative).

Of course, here to prove a thing, is to increase the smaller the number, the more white border selected, you might say is nonsense, but sometimes these things still have to prove, eat shit too emotional understanding of.

First, an unselected front of the white side, \ (A-> B \) , then there must spanning tree \ (A-> B \) of one strand.

Here Insert Picture Description

For us one of the largest weight in terms of the black side (arrow points to the side, if not, then the white side can not be selected), greater than the original value of the white side of black edges, when After adding weights greater than the maximum black side, then this dark side will be completely phased out, which is more than a white border.

Can be found, the weights \ (k \) is an integer enough, not a decimal, if \ (k \) plus \ (1 \) minus \ (1 \) produced a large number white border how to do it? He will speak later.

So we have now is to permit \ (k-1 \) spanning more than \ (k \) spanning tree white edge to be more or the same.

It is for the \ (k \) spanning tree, the more white side to replace the black side, while the original white side, will not disappear, why, because there can be no white edge over his.

While for \ (k \) in terms of the greater of it, you can actually be seen as a black border plus \ (- k \) , and then prove that the number of black edges increment on the line.

So that is correct, and then later ran as long as the cost spanning tree minus the number of white edges on the line.

So in fact we can by half \ (k \) value to control the number of white edges.

But the fact also relates to a question, what is the problem?

If the edge is white and black sides have the same weight, what to choose.

So we just sort of priority in the white border, then asked greater than or equal on the line.

But note that a thing is what does it, is the white side, although the election much, but in fact, we just came number statistics, we finally chose only \ (need \) a white border, the extra was elected a black border , so every time is subtracted \ (need * k \) .

Of course, you can also use this to prove a thing, it is to choose a different number of sides are white in the corresponding \ (k \) is a minimum value.

We can be more than code.

#include<cstdio>
#include<cstring>
#include<algorithm>
#define  N  51000
#define  M  110000
using  namespace  std;
int  fa[N];
int  findfa(int  x)
{
    if(fa[x]==x)return  x;
    return  (fa[x]=findfa(fa[x]));
}
struct  node
{
    int  x,y,c,type;
}a[M];int  len,dep/*白边的系数*/,need;
inline  void  ins(int  x,int  y,int  c,int  type){len++;a[len].x=x;a[len].y=y;a[len].c=c;a[len].type=type;}
inline  bool  cmp(node  x,node  y){return  (x.c+x.type*dep)==(y.c+y.type*dep)?x.type>y.type:(x.c+x.type*dep)<(y.c+y.type*dep);}
int  n,m,ans,zans;
int  check()
{
    sort(a+1,a+m+1,cmp);
    for(int  i=1;i<=n;i++)fa[i]=i;
    int  cnt=0,bai=0;ans=0;
    for(int  i=1;i<=m;i++)
    {
        int  x=a[i].x,y=a[i].y;
        int  tx=findfa(x),ty=findfa(y);
        if(tx!=ty)
        {
            fa[tx]=ty;
            cnt++;ans+=a[i].c;
            if(a[i].type)bai++,ans+=dep;
            if(cnt==n-1)return  bai;
        }
    }
}
int  main()
{
//  freopen("tree7.in","r",stdin);
    scanf("%d%d%d",&n,&m,&need);
    for(int  i=1;i<=m;i++)
    {
        int  x,y,c,type;scanf("%d%d%d%d",&x,&y,&c,&type);
        ins(x+1,y+1,c,type^1);
    }
    int  l=-100,r=100;
    while(l<=r)
    {
        dep=(l+r)/2;
        int  tt=0;
        if((tt=check())>=need)
        {
            
            zans=ans-need*dep,l=dep+1;
        }
        else  r=dep-1;
    }
    printf("%d\n",zans);
    return  0;
}

I believe there are still many people who do not know, but you can skip directly to the next topic to learn WQS half, I believe you will be a flash of.

Official explanation WQS optimization

Scope

Requirements for the title must choose what to how many such problems.

If for \ (f (J) \) , he was just elected to represent the \ (j \) a price.

Then \ (F \) of an image is a function of the convex hull (convex hull or lower):

Here Insert Picture Description

Then you can use the WQS half of it.

Do not look to two points, but can be subject, but very few.

Explain plus proof

And there will be a lot of people have doubts, convex hull should not be a third of it, how he is a half a.

The following uses the convex hull of all, it is the top one in FIG.

Here with a particular god properties of the convex hull is used, that is when the incremental region \ (f (i) -f ( i-1) <= f (i-1) -f (i-2) \ ) , drop is \ (F (I) -f (I +. 1)> = F (I +. 1) -f (I + 2) \) .Why all see Figure it out, if not he is not a convex hull.

We try to \ (f (i) \) plus \ (i * k \) .

Here Insert Picture Description

Then got the image, we can continue to demonstrate a new image (gray) is the convex hull (that is, to meet the nature of the convex hull).

When \ (k> 0 \) when ( \ (K <0 \) as proof):

其实对于两点之差就只是\(+k\),所以还是凸包,但是最大值的位置却右移了!!!!

于是我们发现了一个事情,就是\(k\)越大,最大值的横坐标越大,所以就可以二分\(k\)值,来找到对应的最大值了。

为什么横坐标越大呢,其实最大值的横坐标\(i\)就是最大的且满足\(f(i)-f(i-1)+k>=0\)的地方,所以就是递增的啦。

而对于\(k\)越小,就是横坐标越小。

其实满足这个性质的,他的图像也就是凸包了,比如\(Tree\)这道题目,就可以发现其满足这个性质,然后倒退出他的图像是个凸包。

那么我们现在只需要解决两个问题了。

当坐标都是整数的时候二分的k可以只是整数吗

这个时候我们发现当\(f(i+1)-f(i)=-k\)\(i\)在递减的区域上),那么这个时候\(i\)是整个凸包的最大值。

那么我们发现两点\(y\)坐标之差肯定是整数,那么\(k\)是整数就够了。

当然,坐标是小数就不能这么做了

多个最大值的怎么办

经常会发生一个事情,就是有多个最大值相同,这个时候其实我们在\(check\)的过程中可以默认取到最大的,然后在二分中找到横坐标大于最接近于\(need\)\(k\),那么我们所需要的位置肯定是在\(+k\)的时候最大,因为\(+k-1\)的位置小于\(need\),所以这个位置当最大值的情况只可能在\(+k\)的时候最大了。

当然我之所以不会做\(WQS\)二分套\(WQS\)二分的题目也就是一直搞不懂两个之间的最大值要怎么判断

小结

个人感觉WQS最难的地方就是证明这个图像是否是个凸包了。

但是去网络上看会发现其实WQS有很多专业的术语,可以我是看了做法然后自证的QAQ。

练习

个人感觉WQS最有用的地方就是可以去做有限制的生成树了吧。

当然WQS最牛逼的用处就是优化DP,如果DP优化完后还要用四边形不等式的话,你就可以切黑题了,可惜我不会四边形不等式优化。

1

题目

双倍经验

天哪!第二道题我当时还证不出凸包,然后后面看了一下以前这道题,我以前是怎么证凸包来着?不会是抽象理解吧。

这两道题目如果直接去证明他是个凸包的话,如果吃了屎似的,但是如果你把题目转化一下,排序做差分,就变成了求选\(m\)不相邻的数字之和最小。

那么我们很快想出了\(DP\)状态,\(f[i][j]\)表示到第\(i\)个数字选了\(j\)个数字的答案。

然后我们就可以证明\(g(i)=f[n][i]\)这个函数是凸包了。

其实不是不转化就不能证,只是好像,感觉第一道题在做的时候压根就没有考虑什么是凸包这个问题。

我们可以发现,每次我们都可以去选择一些数字,但是我们其实发现了一些问题就是选这个数字可能需要一些数字的让步,让他们去选其他数字,甚至还会去选比以前选的数字小的数字!!!!

凸包证明,不会

#include<cstdio>
#include<cstring>
#define  N  110000
using  namespace  std;
typedef  long  long  LL;
struct  node
{
    LL  f;
    LL  mat;/*匹配数*/
}dp[N][2]/*0选,1不选*/;
LL  n,m,a[N];
node  check(LL  x)
{
    for(LL  i=2;i<=n;i++)
    {
        dp[i][0].f=dp[i-1][1].f+a[i]-a[i-1]+x;dp[i][0].mat=dp[i-1][1].mat+1;
        //选
        if(dp[i-1][0].f<dp[i-1][1].f  ||  (dp[i-1][0].f==dp[i-1][1].f  &&  dp[i-1][0].mat>dp[i-1][1].mat))dp[i][1]=dp[i-1][0];
        else  dp[i][1]=dp[i-1][1];
        //不选 
    }
    if(dp[n][0].f<dp[n][1].f  ||  (dp[n][0].f==dp[n][1].f  &&  dp[n][0].mat>dp[n][1].mat))return  dp[n][0];
    else  return  dp[n][1];
}
int  main()
{
    scanf("%d%d",&n,&m);
    for(LL  i=1;i<=n;i++)scanf("%d",&a[i]);
    LL  l=a[1]-a[n]-1,r=0,mid;
    node  ans;
    while(l<=r)
    {
        mid=(r-l)/2+l;
        node  now=check(mid);
        if(now.mat>=m)ans=now,ans.f-=(LL)mid*m,l=mid+1;
        else  r=mid-1;
    }
    printf("%lld\n",ans.f);
    return  0;
}

差不多的题目

只不过需要设\(f[i][0/1][0/1]\)表示到第\(i\)个数字,第一个数字选了没选,第\(i\)个数字选了没选的情况。

其实就是刷题的时候没看到。

练习2

黑题!!!!

这道题目我差点就做出来了QAQ。

其实如果你知道是\(WQS\)还是比较好做的。

首先们可以发现其实题目要求就是选出在树上的\(k+1\)条点不重复的路径之和的最大值。

那么我们就可以设\(f[i][k]\)表示表示到了第\(i\)点选了\(k\)条路径的情况。

但是我们发现我们没办法拼接路径!!!!

So we reproduce at \ (the DP \) state is \ (f [i] [k ] [0/1/2] \) case, which is the first \ (I \) points on the route is not the case, point \ (I \) in an unformed on the path, and is on the path of the \ (1/2 \) of the case, meaning that no unformed counted \ (K \) of the number of.In fact, I personally feel included a can do.

Then \ (DP \) equation own push it, it should not be difficult.

So what we are going to permit what is it?

I.e. \ (G (I) = max (F [. 1] [I] [0], F [. 1] [I] [. 1], F [. 1] [I] [2]) \) ( \ (. 1 \) is the root node) is a convex hull.

So this we are very happy to witness the readers. (fog

Then every time we just need to put the path in the shaping of unformed \ (k + \) , then half \ (k \) on the line.

#include<cstdio>
#include<cstring>
#include<algorithm>
#define  N  310000
#define  NN  610000
using  namespace  std;
typedef  long  long  LL;
template  <class  T>
inline  T  mymin(T  x,T  y){return  x<y?x:y;}
template  <class  T>
inline  T  mymax(T  x,T  y){return  x>y?x:y;}
//DP数组 
struct  node
{
    LL  sh;//表示分的段数,1,2不算一段 
    LL  x;//表示的是保留一条边减去保留两条然后加上到父亲的边权。 
    node(LL  xx=0,LL  yy=0){x=xx;sh=yy;}
}f[N][3];
inline  bool  operator<(node  x,node  y){return  x.x!=y.x?(x.x<y.x):(x.sh<y.sh);}
inline  bool  operator>(node  x,node  y){return  x.x!=y.x?(x.x>y.x):(x.sh>y.sh);}
inline  node  operator+(node  x,node  y){return  node(x.x+y.x,x.sh+y.sh);}
struct  bian
{
    LL  y,next;
    LL  c;
}a[NN];LL  len=1,last[N];
void  ins(LL  x,LL  y,LL  c){len++;a[len].y=y;a[len].c=c;a[len].next=last[x];last[x]=len;}
LL  fu/*表示每次断边的附加权值*/,top;
bool  v[N];//是否有被查到 
node  an;
void  dfs(LL  x,LL  fa)//处理出最简单的DP 
{
    if(x==6)
    {
        x++;
        x--;
    }
    f[x][0]=f[x][2]=node(0,0);f[x][1]=node((LL)-9999999999999999,0/*自己自成一段*/);
    for(LL  k=last[x];k;k=a[k].next)
    {
        LL  y=a[k].y;
        if(y!=fa)dfs(y,x);//处理出子树信息 
    }
    for(LL  k=last[x];k;k=a[k].next)
    {
        LL  y=a[k].y;
        if(y!=fa)
        {
            node  no1=mymax(f[y][0],f[y][1]);no1.x+=a[k].c;//使用这条边的最大值 
            node  no2=mymax(f[y][1],f[y][2]);no2=mymax(node(no2.x+fu,no2.sh+1)/*断边*/,f[y][0]/*不断边,也不使用*/);//表示不使用这条边,割掉,或者直接不用 
            //询问其能不能多添加一条边 
            f[x][2]=mymax(f[x][2]+no2,f[x][1]+no1);
            f[x][1]=mymax(f[x][1]+no2,f[x][0]+no1/*表示使用这条边*/); 
            f[x][0]=f[x][0]+no2;//表示使用其中的0条边。 
        }
    }
}
LL  n,m;//表示点数和边数。 
bool  check()
{
    dfs(1,0);//表示遍历1所在的子树。
    f[1][1].sh++;f[1][2].sh++;
    f[1][1].x+=fu;f[1][2].x+=fu;
    an=mymax(f[1][0],mymax(f[1][1],f[1][2]));
    return  an.sh>=m;
}
int  main()
{
//  freopen("1.in","r",stdin);
//  freopen("1.out","w",stdout);
    scanf("%lld%lld",&n,&m);m++;
    for(LL  i=1;i<n;i++)
    {
        LL  x,y;LL  c;scanf("%lld%lld%lld",&x,&y,&c);
        ins(x,y,c);ins(y,x,c);
    }
    LL  l=-1e13,r=1e13,zans=0;
    while(l<=r)
    {
        fu=(l+r)/2;
        if(check())
        {
            zans=an.x-(LL)m*fu;
            r=fu-1;
        }
        else  l=fu+1;
    }
    printf("%lld\n",zans);
    return  0;
}

In fact, I'm curious about is how nlog code of 1min ran.

pit

  1. Exercise complement convex hull 1 and 2 demonstrate practice

Guess you like

Origin www.cnblogs.com/zhangjianjunab/p/11962831.html