题面简述

一条线上等距地分布着

n

n

n 老鼠和

m

m

m 洞(

m

n

m\geq n

m≥n),这连续

n

+

m

n+m

n+m 个位置上要么是老鼠要么是洞,一个老鼠进一个洞,代价是所有老鼠的路程和,问最小代价。

n

+

m

5000

n+m\leq 5000

n+m≤5000

题解

平方做法

每个老鼠匹配一个洞,我们可以发现存在最优方案,使得路径两两之间不包含,这样一来,从左到右老鼠匹配的洞位置就是单增的。可以用调整法证明,如果两条路径包含,那么把终点位置互换,如果路径方向不同则更优(少了2×中间段长度),如果路径方向相同,根据绝对值算出来答案是相等的。

有了这个最优方案的特点后,我们就可以设计一个

D

P

\rm DP

DP ,令

d

p

[

i

]

[

j

]

dp[i][j]

dp[i][j] 表示前

i

i

i 个老鼠进前

j

j

j 个洞的最小代价,最终答案就是

d

p

[

n

]

[

m

]

dp[n][m]

dp[n][m]。不妨设

d

i

s

(

i

,

j

)

dis(i,j)

dis(i,j) 表示第

i

i

i 只老鼠进第

j

j

j 个洞的路径长度,状态转移如下:

d

p

[

i

]

[

j

]

=

min

{

d

p

[

i

]

[

j

1

]

,

d

p

[

i

1

]

[

j

1

]

+

d

i

s

(

i

,

j

)

}

dp[i][j]=\min\Big\{dp[i][j-1]~,~dp[i-1][j-1]+dis(i,j)\Big\}

dp[i][j]=min{dp[i][j−1] , dp[i−1][j−1]+dis(i,j)}

这应该很好理解,第

i

i

i 只鼠与第

j

j

j 个洞匹配,就是右边的式子,否则就是左边。

顺便提一下,有的做法是先推了一个

O

(

n

)

O(n)

O(n) 转移的

n

2

n^2

n2 DP,然后用前缀优化的,其实完全没这个必要。

然后,我们发现

d

p

[

i

]

[

j

]

dp[i][j]

dp[i][j] 的转移需要的两个前驱状态和它的曼哈顿距离挺小的 (

(

i

,

j

1

)

(i,j-1)

(i,j−1) 和

(

i

1

,

j

1

)

(i-1,j-1)

(i−1,j−1)),因此,我们用一两个小变量存一下

d

p

[

i

1

]

[

j

1

]

dp[i-1][j-1]

dp[i−1][j−1] ,能省掉第一维。由于数组访问的原理特性,你再卡卡常应该就能跑

30

m

s

\rm30~ms

30 ms 左右。

CODE

#include<set>
#include<queue>
#include<cmath>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 5005
#define ENDL putchar('\n')
#define LL long long
#define DB double
#define lowbit(x) ((-x) & (x))
LL read() {
LL f = 1,x = 0;char s = getchar();
while(s < '0' || s > '9') {if(s=='-')f = -f;s = getchar();}
while(s >= '0' && s <= '9') {x=x*10+(s-'0');s = getchar();}
return f * x;
}
int n,m,i,j,s,o,k;
int a[MAXN],b[MAXN],c[MAXN],cnb,cnc;
int Abs(int x) {return x < 0 ? -x:x;}
int dp[MAXN];
int main() {
n = read();
for(int i = 1;i <= n;i ++) {
a[i] = read();
if(a[i] == 1) b[++ cnb] = i;
else c[++ cnc] = i;
}
for(int i = 1;i <= cnb;i ++) {
int p = dp[0],pp; dp[0] = 0x3f3f3f3f;
for(int j = 1;j <= cnc;j ++) {
pp = dp[j]; dp[j] = min(dp[j-1],p + Abs(b[i]-c[j]));
p = pp;
}
}
printf("%d\n",dp[cnc]);
return 0;
}

线性做法

我改过的题意简述是不是看起来很熟悉?

没错,这就是比较经典的模拟费用流板题。

与上面最主要的不同点是(我认为的)把绝对值符号拆开

我们先换了一种

D

P

\rm DP

DP 方式,

d

p

[

i

]

[

j

]

dp[i][j]

dp[i][j] 表示前

i

i

i 个位置有

j

j

j 个洞需要匹配(

j

j

j 为负是表示有

j

-j

−j 个老鼠) ,已经匹配的路径和加上

j

j

j 个洞(或

j

-j

−j 只鼠)的坐标和最大值

其实是用了一点贪心的优化。由于路径不包含,所以前面不会同时有洞和老鼠向 i 后面连边。这样,DP 的转移就是决策唯一的了,这就不是 DP 而是类似递推了。因此用一些较复杂的标记可以把它优化成

O

(

n

)

O(n)

O(n)。

更详细的可以看上面的模拟费用流链接或是下面的

R

a

n

k

1

\rm Rank 1

Rank1 代码(30 ms)

CODE(Pyqe)

#include <bits/stdc++.h>

using namespace std;

long long n,ps[100069];
priority_queue<long long> pq; int main()
{
long long i,ii,k,z=0; scanf("%lld",&n);
for(i=1;i<=n;i++)
{
scanf("%lld",&k);
ps[i]=ps[i-1]+!k*2-1;
}
for(i=1;i<=n;i++)
{
k=min(max(ps[i],0ll),ps[n]);
z+=abs(ps[i]-k);
if(i-1)
{
z+=max(pq.top()-k,0ll);
}
for(ii=0;ii<2;ii++)
{
pq.push(k);
}
pq.pop();
}
printf("%lld\n",z);
}

最新文章

  1. 2048游戏C语言代码
  2. Eclipse svn插件包
  3. ORACLE数据库删除表中记录报record is locked by another user
  4. js随机生成字母数字组合的字符串 随机动画数字
  5. HDU 5724 Chess (sg函数)
  6. mac在线恢复教程
  7. 【转】Java方向如何准备BAT技术面试答案(汇总版)
  8. html学习第一弹の常用标签的归类
  9. Linux中文件夹访问权限不足
  10. Python-递归、三元表达式列表生成式等
  11. 复旦大学2016--2017学年第一学期(16级)高等代数I期末考试第七大题解答
  12. Pyhon文件的用途
  13. C# 方法参数 out、ref、param 详解
  14. Python与矩阵论——特征值与特征向量
  15. Mysql初级第二天(wangyun)
  16. ofstream的使用方法--超级精细。C++文件写入、读出函数(转)
  17. Mac OS 10.12 - 在VMwear Workstation12.5.2中大写键和中英文输入法的切换!
  18. poj 1995 Raising Modulo Numbers 题解
  19. Android下点亮LED
  20. 《DSP using MATLAB》示例Example7.7

热门文章

  1. CabloyJS v3.1.0支持集群及更多 &#127881;
  2. NodeJS全栈开发利器:CabloyJS究竟是什么
  3. Camunda开源流程引擎快速入门——Hello World
  4. 重学ES系列之函数优化
  5. mybatis查询mysql 数据库中 BLOB字段,结果出现乱码
  6. 03 转换css元素的类别
  7. zabbix监控apache80端口
  8. bat-winget-win平台的软件包管理器
  9. Spring Boot 整合 minio(一步到位)
  10. NC202492 仓库选址