A、方伯伯的玉米田
http://www.lydsy.com/JudgeOnline/problem.php?id=3594
题解
做出此题的关键是要想到,每次选择一个区间加1格高度时,区间的右端点在最后一个玉米处肯定是最优的。
于是可以用DP来做,用
f[i][j]
表示前
i
个玉米,提升
这个DP方程的正确性是显然的,但是就这样做复杂度是
O(n2k2)
,肯定会TLE的,因此我们需要优化,我们可以维护一个二维树状数组
bit[][]
,DP时按照
i
的顺序进行DP,对于当前的
另外注意一些边界细节,还有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
向
而题目要求的是
(旧费用−新费用)调整次数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;
}