[2014年四川省选]简要题解

98 篇文章 0 订阅
49 篇文章 0 订阅

A、方伯伯的玉米田

http://www.lydsy.com/JudgeOnline/problem.php?id=3594

题解

做出此题的关键是要想到,每次选择一个区间加1格高度时,区间的右端点在最后一个玉米处肯定是最优的。
于是可以用DP来做,用 f[i][j] 表示前 i 个玉米,提升j次,并且每次提升的区间的右端点都在第 n 个玉米处的LIS最大值。可以得到DP方程:
f[i][j]=max{f[k][l]+1|k<=i,l<=j,hk+l<=hi+j}
这个DP方程的正确性是显然的,但是就这样做复杂度是 O(n2k2) ,肯定会TLE的,因此我们需要优化,我们可以维护一个二维树状数组 bit[][] ,DP时按照 i 的顺序进行DP,对于当前的i而言, bit[a][b]=max{f[k][l]},k<i,hk+l<a ,这个BIT,我们可以在每次DP得到当前最优值后,将当前最优值放进BIT里更新即可,优化后的复杂度将原来的复杂度中一个 n 一个k变成了 logn2logk2

另外注意一些边界细节,还有BIT数组开的大小,否则会WA。

代码


#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>

#define MAXN 6500 //!!!!注意范围
#define MAXM 550
#define lowbit(x) ((x)&(-(x)))

using namespace std;

int bit[MAXN][MAXM];
int h[11000];

void update(int x,int y,int val)
{
    while(x<MAXN)
    {
        int tmpy=y;
        while(tmpy<MAXM)
        {
            bit[x][tmpy]=max(bit[x][tmpy],val);
            tmpy+=lowbit(tmpy);
        }
        x+=lowbit(x);
    }
}

int query(int x,int y)
{
    int ans=0;
    while(x>0)
    {
        int tmpy=y;
        while(tmpy>0)
        {
            ans=max(ans,bit[x][tmpy]);
            tmpy-=lowbit(tmpy);
        }
        x-=lowbit(x);
    }
    return ans;
}

int main()
{
    int n,K;
    scanf("%d%d",&n,&K);
    for(int i=1;i<=n;i++)
        scanf("%d",&h[i]);
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=K;j>=0;j--) //!!!!!!
        {
            int tmp=query(h[i]+j,j+1)+1; //!!!!!
            ans=max(ans,tmp);
            update(h[i]+j,j+1,tmp);
        }
    }
    printf("%d\n",ans);
    return 0;
}

D、方伯伯运椰子

题解

刚开始看到这个题感觉非常地不可做,但是看了别人题解后发现做法其实很简单,就是个比较裸的分数规划。

首先我们可以把题目的问题看作是搬运每个点的流量,每个点的流量就是每个点经过的流量大小。这样思考便能很容易建立一个费用流的模型:对于原图中每条边 u>v u v连容量正无穷,边权为 a+d 的边( uv 运送一单位流量,付出代价 a ,同时还要付出这个流量的花费d),若 c>0 (该边存在流量), v u连容量为 c ,边权为bd的边( vu 运送一单位流量,付出代价 b ,同时减少了这个流量的花费d)。这个费用流,在给定每条边的流量后,就能得到这样改变原图后的总费用的变化值。

而题目要求的是 ()max ,即最优比率,而不是最优费用流,因此不能用费用流算法解决此题。
引出一个消圈定理:若残量网络里存在负权环,那么当前的费用流一定不是最优解,因为给负权环增加流量后,费用会减少,流量会增大。
最终的答案就是在刚刚建立的费用流模型中找一个 最少的一个环,这就是一个很裸的分数规划问题了,在HNOI中也有考过。

这个分数规划问题的做法就是二分答案,并用当前的 () 的值加到每条边的边权上。若找得到负环,表明当前的值可行,否则不可行。

代码

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>

#define MAXE 7000
#define MAXV 600
#define EPS 1e-4

using namespace std;

int n,m;

struct edge
{
    int u,v,next;
    double w;
}edges[MAXE];

int head[MAXV],nCount=0;

void AddEdge(int U,int V,double W)
{
    edges[++nCount].u=U;
    edges[nCount].v=V;
    edges[nCount].w=W;
    edges[nCount].next=head[U];
    head[U]=nCount;
}

double dist[MAXV];
bool vis[MAXV];

bool SPFA(int u) //返回true表示找到了环
{
    vis[u]=true;
    for(int p=head[u];p!=-1;p=edges[p].next)
    {
        int v=edges[p].v;
        if(dist[u]+edges[p].w<dist[v])
        {
            dist[v]=dist[u]+edges[p].w;
            if(vis[v]) return true;
            if(SPFA(v)) return true;
        }
    }
    vis[u]=false;
    return false;
}

bool check(double addv)
{
    memset(dist,0,sizeof(dist));
    memset(vis,false,sizeof(vis));
    for(int i=1;i<=nCount;i++) edges[i].w+=addv;
    for(int i=1;i<=n;i++)
    {
        if(SPFA(i))
        {
            for(int i=1;i<=nCount;i++) edges[i].w-=addv;
            return true;
        }
    }
    for(int i=1;i<=nCount;i++) edges[i].w-=addv;
    return false;
}

int main()
{
    memset(head,-1,sizeof(head));
    scanf("%d%d",&n,&m);
    n+=2;
    for(int i=1;i<=m;i++)
    {
        int u,v,a,b,c,d;
        scanf("%d%d%d%d%d%d",&u,&v,&a,&b,&c,&d);
        AddEdge(u,v,b+d);
        if(c>0) AddEdge(v,u,a-d);
    }
    double lowerBound=0,upperBound=1e9;
    while(upperBound-lowerBound>EPS)
    {
        double mid=(lowerBound+upperBound)/2;
        if(check(mid)) lowerBound=mid;
        else upperBound=mid;
    }
    printf("%.2f\n",lowerBound);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值