UVA1169 Robotruck

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 个垃圾,并且在第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 )
其中 0j<i 0 ≤ j < i cost(0,j) c o s t ( 0 , j ) 表示从原点走到 j j 的代价,s[i] cost(i1i) c o s t ( i − 1 , i ) 的前缀和,这些都可以预处理,要保证 ik=j+1w[k] ∑ k = j + 1 i w [ k ] 不超过 C C ,那这不就成了sliding window吗?
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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值