动态规划测试test20170518

来源:互联网 发布:类似皮影客的软件 编辑:程序博客网 时间:2024/05/19 14:56

这里写图片描述
这里写图片描述

题意:
一个长度为N的序列(每个元素是(ai,bi)这样的数对),连续地分成若干组。每组左右边界是(l1,r1),(l2,r2),⋯,(lp,rp),满足li=ri1+1,liri,l1=1,rp=n。分组必须满足两个条件:前面组的元素的b值比后面组元素的a值大;令Mi为第i个组内最大的a,所有Mi的和不超过limit。令Si为第i个组的元素的b的和,最小化maxSi

题解:

二分答案。
能否破除b和a的关系呢?
考虑什么情况下必须捆绑在一起。
如果ai>bj,那么任意bkbj,都必须与ai捆绑在一起,否则不符合题意。
所以考虑排序bj,然后从大到小扫。
然后剩下的就没有限制了,于是问题就变成,给出一个数列,(每个组a的最大值)的和不能超过limit,(各组内b的的和)的最大值最小。
然后一个单调队列优化DP,具体细节看代码。
想交OJ的话可以看这个SPOJ.com - Problem SEQPAR2
然后lcy大神用线段树,我跑了8.72s,而Ta却只要4s,线段树题解看Ta的吧:http://blog.csdn.net/never_see/article/details/72802369

代码:

#include <cstdio>#include <cstring>#include <algorithm>#define Max(a,b) ((a)>(b)?(a):(b))#define Min(a,b) ((a)<(b)?(a):(b))typedef long long LL;const int size = 50000+10;const LL INF = 0x7fffffff;int n,limit,q[size],a[size],b[size];int dp[size],p1[size],p2[size];bool check(int m) {    int i,j=1,k,s=0,f=0,r=0;    for(i=1; i<=n; i++) {        s+=b[i];        while(s>m) s-=b[j++];        if(j>i) return 0;        while(f<r and a[q[r-1]]<=a[i]) --r;        while(f<r and q[f]<j) ++f;        q[r++]=i;        dp[i]=dp[j-1]+a[q[f]];        for(k=f+1; k<r; k++)            dp[i]=Min(dp[i],dp[q[k-1]]+a[q[k]]);    }    return dp[n]<=limit;}bool cmp(int x,int y) {    return b[x]<b[y];}int main() {    int i, j, l, r, mid;    freopen("gold.in","r",stdin);    freopen("gold.out","w",stdout);    scanf("%d%d", &n, &limit);    for (i = 1; i <= n; ++i)        scanf("%d%d", &a[i], &b[i]), p1[i] = p2[i] = i;    std::sort(p1 + 1, p1 + n + 1, cmp);    for (j = 1, i = n; i >= 1; --i)        for (; j <= n && b[p1[j]] <= a[i]; ++j)            p2[p1[j]] = i;    for (i = 1, j = 1; i <= n; i = l, ++j) {        a[j] = a[i];        b[j] = b[i];        for (l = i + 1, r = Max(p2[i], i); l <= r; ++l) {            a[j] = Max(a[j], a[l]);            b[j] += b[l];            r = Max(r, p2[l]);        }    }    n = j - 1;    for (l = 0, r = INF; l < r;) {        mid = l + r >> 1;        if (check(mid)) r = mid;        else l = mid + 1;    }    printf("%d\n", l);    return 0;}

这里写图片描述
这里写图片描述

题解:

这个题目有两种做法,一种直接用斜率优化,另一种在斜率优化的基础上进行点分治优化。
然后这个题目在NOI2014上被加强了为购票,题目和做法差不多,可以参见这个题目的做法。
代码:

#include <cstdio>#include <cstring>#include <algorithm>#define Max(a,b) ((a)>(b)?(a):(b))#define Min(a,b) ((a)<(b)?(a):(b))typedef long long LL;const int N = 100000+10;const LL INF = 1LL<<60;int n,g[N],nxt[N<<1],v[N<<1],w[N<<1],ok[N<<1],tot=1,son[N],f[N];int size,now,fa[N],V[N];int q[N],anc[N],t;LL ans[N],d[N],W[N];inline int read(int &in) {    in=0;char ch=getchar();    for(;ch>='0'&&ch<='9';ch=getchar())    in=in*10+ch-'0';    return in;}inline void add_edge(int x,int y,int z) {    v[++tot]=y;w[tot]=z;nxt[tot]=g[x];ok[tot]=1;g[x]=tot;}inline int up(LL &x,LL y) { if(x>y) x=y; }inline double pos(int x,int y) {    return (double)(ans[y]-ans[x])/(double)(d[y]-d[x]);}namespace my_self {    inline void find_root(int x,int y) {    son[x]=1;f[x]=0;    for(int i=g[x];i;i=nxt[i])        if(ok[i] and v[i]!=y) {        find_root(v[i],x);son[x]+=son[v[i]];        if(son[v[i]]>f[x]) f[x]=son[v[i]];        }    if(size-son[x]>f[x]) f[x]=size-son[x];    if(f[x]<f[now]) now=x;    }    inline void erfen(int x) {    int l=1,r=t-1,tmp=t,mid;    while(l<=r) {        mid=(l+r)>>1;        if((double)V[x]<=pos(q[mid],q[mid+1]))        r=(tmp=mid)-1;        else l=mid+1;    }    up(ans[x],W[x]+ans[q[tmp]]-d[q[tmp]]*V[x]);    }    inline void dfs(int x) {    W[x]+=d[x]*V[x];    for(int i=g[x];i;i=nxt[i])        if(v[i]!=fa[x])        d[v[i]]=d[fa[v[i]]=x]+w[i],dfs(v[i]);    }    inline void cal(int x,int y) {    erfen(x);    for(int i=g[x];i;i=nxt[i])        if(ok[i]&&v[i]!=y)cal(v[i],x);    }    inline void cal2(int x,int y) {    up(ans[x],W[x]+ans[y]-d[y]*V[x]);    for(int i=g[x];i;i=nxt[i])        if(ok[i]&&v[i]!=fa[x])cal2(v[i],y);    }    inline void solve(int x) {    f[0]=size=son[x];    find_root(x,now=0);    int root=now,ta;    if(root!=x) {        for(int i=g[root];i;i=nxt[i])        if(v[i]==fa[root]) {            ok[i]=ok[i^1]=0,solve(x);break;        }        int i;        for(ta=0,i=fa[root];;i=fa[i]) {        anc[++ta]=i;if(i==x) break;        }        for(t=0;ta;q[++t]=anc[ta--])        while(t>1&&pos(anc[ta],q[t])<pos(q[t],q[t-1]))            t--;        if(root>1) erfen(root);        while(t>1 and pos(root,q[t])<pos(q[t],q[t-1]))          t--;        q[++t]=root;        for(int i=g[root];i;i=nxt[i]) if(ok[i]) cal(v[i],root);        for(int i=g[root];i;i=nxt[i]) if(ok[i]) ok[i^1]=0,solve(v[i]);    }    else        for(int i=g[x];i;i=nxt[i])        if(ok[i]) ok[i^1]=0,cal2(v[i],x),solve(v[i]);    }}int main() {    freopen("harbingers.in","r",stdin);    freopen("harbingers.out","w",stdout);    read(n);    for(int i=1;i<n;i++) {    int x,y,z;read(x);read(y);read(z);    add_edge(x,y,z);add_edge(y,x,z);    }    for(int i=2;i<=n;i++) {    int x;read(x);read(V[i]);W[i]=x;ans[i]=INF;    }    son[1]=n;my_self::dfs(1);my_self::solve(1);    for(int i=2;i<=n;i++)    printf("%lld ",ans[i]);    return 0;}

这里写图片描述

原题APIO 2010特别行动队

斜率优化经典题

代码:

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<cmath>#include<algorithm>#define F(i,j,n) for(int i=j;i<=n;i++)#define D(i,j,n) for(int i=j;i>=n;i--)typedef long long ll;const int maxn = 100000+10;using namespace std;int n,l,r;ll a,b,c,q[maxn],f[maxn],s[maxn];inline int read(){    int x=0,f=1;char ch=getchar();    while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}    while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}    return x*f;}inline double getk(int x,int y){    return (double)((f[x]+a*s[x]*s[x]-b*s[x])-(f[y]+a*s[y]*s[y]-b*s[y]))/(double)(s[x]-s[y]);}int main(){    n=read();a=read();b=read();c=read();    F(i,1,n) s[i]=s[i-1]+read();    l=r=1;q[1]=0;    F(i,1,n)    {        while (l<r&&getk(q[l],q[l+1])>a*s[i]*2) l++;        f[i]=f[q[l]]+a*s[q[l]]*s[q[l]]-b*s[q[l]]-a*s[i]*s[q[l]]*2+a*s[i]*s[i]+b*s[i]+c;        while (l<r&&getk(q[r-1],q[r])<getk(q[r],i)) r--;        q[++r]=i;    }    printf("%lld\n",f[n]);    return 0;}
原创粉丝点击