CCPC - k题(dp)

本题目的意思:

给定n个位置,每个位置有一个二元组,要求确定n个位置的属性(选1或者是2),使得每个二元组的1属性和2属性去最近的位置,总代价最小(代价 属性值*移动距离)

分析:

可以尝试如此规划,定义d[ i ][ j ]为当前修到了i位置,i位置的属性为j,那么下面的一段连续路径要修的属性为(3-j),这一段路径上的属性为(3-j)的不需要移动,剩下的要移动到两边。

这个题目难得地方在于怎样设计状态使得具有很好的规划性质。

附: 交题点击打开链接

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define rep1(i,x,y) for(int i=x;i<=y;i++)
#define rep(i,n) for(int i=0;i<(int)n;i++)
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int N = 4010;
int n;
struct node
{
    ll pre[N],pp[N],suf[N],ss[N];
    void init()
    {
        pre[0]=pp[0]=suf[n+1]=ss[n+1]=0;
        rep1(i,1,n) pre[i]=pre[i-1]+pre[i],pp[i]=pre[i]+pp[i-1];
        for(int i=n; i>=1; i--) suf[i]=suf[i+1]+suf[i],ss[i]=ss[i+1]+suf[i];
    }
    ll get_lr(int i,int j)
    {
        int mid=(j-i-1)/2+i , llen = mid-i , rlen = j-mid-1;
        ll sum = 0;
        sum+=ss[i+1]-ss[mid+1]-suf[mid+1]*llen;
        sum+=pp[j-1]-pp[mid]-pre[mid]*rlen;
        return sum;
    }
    ll get(int i,int j)
    {
        if(i == 0) return pp[j-1];
        else if(j == n+1) return ss[i+1];
        return get_lr(i,j);
    }
} T,P;
ll d[N][2];
bool vis[N][2];
ll dp(int i,int j)
{
    if(vis[i][j]) return d[i][j];
    vis[i][j] = true;
    if(i == n) return d[i][j] = 0;
    int lim = (i==0 ? n-1 : n);
    rep1(k,i+1,lim)
    {
        ll add=(j==0 ? T.get(i,k+1):P.get(i,k+1));
        if(k == i+1) d[i][j] = dp(k,j^1)+add;
       else  d[i][j] = min(d[i][j],dp(k,j^1)+add);
    }
    return d[i][j];
}
int main()
{
    int Ta,kase=1;
    scanf("%d",&Ta);
    while(Ta--)
    {
        scanf("%d",&n);
        int x,y;
        rep1(i,1,n) scanf("%d %d",&x,&y),T.pre[i]=T.suf[i]=x,P.pre[i]=P.suf[i]=y;
        T.init();
        P.init();
        memset(vis,false,sizeof(vis));
        cout<<"Case #"<<kase++<<": "<<min(dp(0,0),dp(0,1))<<endl;
    }
    return 0;
}




  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值