Source
https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=3610
Solution1
f[i][j]瞎搞一搞就行了
Solution2
刘汝佳说
O(NC)
O
(
N
C
)
会超时,然而才
107
10
7
啊,怎么会超时呢
用
f[i]
f
[
i
]
表示我搞完了前
i
i
个垃圾,并且在第个位置倒垃圾的最小代价
那么
f[i]=min{f[j]+cost(0,j+1)+s[i]−s[j+1]+cost(i,0)}
f
[
i
]
=
m
i
n
{
f
[
j
]
+
c
o
s
t
(
0
,
j
+
1
)
+
s
[
i
]
−
s
[
j
+
1
]
+
c
o
s
t
(
i
,
0
)
}
=min{f[j]−s[j+1]+cost(0,j+1)}+s[i]+cost(i,0)
=
m
i
n
{
f
[
j
]
−
s
[
j
+
1
]
+
c
o
s
t
(
0
,
j
+
1
)
}
+
s
[
i
]
+
c
o
s
t
(
i
,
0
)
其中
0≤j<i
0
≤
j
<
i
,
cost(0,j)
c
o
s
t
(
0
,
j
)
表示从原点走到
j
j
的代价,是
cost(i−1,i)
c
o
s
t
(
i
−
1
,
i
)
的前缀和,这些都可以预处理,要保证
∑ik=j+1w[k]
∑
k
=
j
+
1
i
w
[
k
]
不超过
C
C
,那这不就成了吗?
令
c[j]=f[j]−s[j+1]+cost(0,j+1)
c
[
j
]
=
f
[
j
]
−
s
[
j
+
1
]
+
c
o
s
t
(
0
,
j
+
1
)
现在方程变为
f[i]=min{c[j]}+(s[i]+cost(i,0)),(k<i,∑ik=j+1w[k])
f
[
i
]
=
m
i
n
{
c
[
j
]
}
+
(
s
[
i
]
+
c
o
s
t
(
i
,
0
)
)
,
(
k
<
i
,
∑
k
=
j
+
1
i
w
[
k
]
)
维护一个单调递增的双端队列,每进来一个元素,比它大的就永远不会用到,因此从队尾进去,不断踢掉那些不小于它的元素,用的时候,不断的从队首取元素,取到第一个满足
∑ik=j+1w[k]
∑
k
=
j
+
1
i
w
[
k
]
即可使用它更新
f[i]
f
[
i
]
,不合法的直接扔掉即可
每个元素只进队列一次,出队列一次,时间复杂度
O(n)
O
(
n
)
Code1
//动态规划
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cctype>
#include <cmath>
#define ll long long
#define clear(x) memset(x,0,sizeof(x))
#define maxn 100010
#define maxc 110
#define inf (1ll<<60)
using namespace std;
ll n, m, x[maxn], y[maxn], C, w[maxn], f[maxn][maxc];
inline ll read(ll x=0)
{
char c, f=1;
for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-48;
return x*f;
}
inline ll d(ll a, ll b)
{
return abs(x[a]-x[b])+abs(y[a]-y[b]);
}
void dp()
{
ll i, j, ans=inf;
for(i=0;i<=n+1;i++)for(j=0;j<=C;j++)f[i][j]=inf;
f[0][0]=0;
for(i=0;i<=n;i++)for(j=0;j<=C;j++)
{
f[i+1][w[i+1]]=min(f[i+1][w[i+1]],f[i][j]+d(i,0)+d(0,i+1));
if(j+w[i+1]<=C)f[i+1][j+w[i+1]]=min(f[i+1][j+w[i+1]],f[i][j]+d(i,i+1));
}
for(j=1;j<=C;j++)ans=min(ans,f[n+1][j]);
printf("%lld\n",ans);
}
int main()
{
ll i;
for(ll T=read();T--;)
{
C=read(), n=read();
for(i=1;i<=n;i++)x[i]=read(), y[i]=read(), w[i]=read();
dp();
if(T)printf("\n");
}
return 0;
}
Code2
//单调队列优化DP
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cctype>
#include <cmath>
#define ll long long
#define cl(x) memset(x,0,sizeof(x))
#define maxn 100010
#define inf (1ll<<60)
using namespace std;
ll n, m, x[maxn], y[maxn], C, w[maxn], f[maxn], c[maxn], head, tail, s[maxn];
struct pos{ll w, c;}q[maxn];
inline ll read(ll x=0)
{
char c, f=1;
for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-48;
return x*f;
}
inline ll d(ll a, ll b)
{
return abs(x[a]-x[b])+abs(y[a]-y[b]);
}
void init()
{
ll i;
C=read(), n=read();
for(i=1;i<=n;i++)x[i]=read(), y[i]=read(), w[i]=w[i-1]+read();
for(i=1;i<=n;i++)s[i]=s[i-1]+d(i-1,i);
}
void DP()
{
ll i;
pos t;
t=(pos){w[0],f[0]-s[1]+d(0,1)};
q[head=tail=1,tail++]=t;
for(i=1;i<=n;i++)
{
for(;w[i]-q[head].w>C;head++); //踢掉队首不合法元素
f[i]=s[i]+d(i,0)+q[head].c;
t=(pos){w[i],f[i]-s[i+1]+d(0,i+1)};
for(;head<tail and q[tail-1].c>=t.c;tail--);
q[tail++]=t;
}
printf("%d\n",f[n]);
}
int main()
{
for(int T=read();T--;)
{
init();
DP();
if(T)printf("\n");
}
return 0;
}