洛谷 3140 (USACO2016Dec金组 T2 Circular Barn Revisited) 题解

该博客介绍了USACO2016Dec金组的一道题目,题号为3140。博主详细解析了题意,描述了在环形排列的点上放置源并分配牛的策略,以使总代价达到最小。数据部分展示了输入输出格式。博主提出使用动态规划(DP)解决此问题,初始状态设置以及如何断环成链的方法。博客还提及存在O(n^3k)的时间复杂度解决方案,并提到了更优的O(n^2k)斜率优化策略,但表示自己并未掌握。

题意简述

在一个n(n&lt;=100)n(n&lt;=100)n(n<=100)个点的环中放置k(k&lt;=7)k(k&lt;=7)k(k<=7)个源,这kkk个源中每个都珂以按顺时针方向跑出去一些牛,假设这一个源在xxx,跑出去的牛要到yyy,则每头牛代价都是∣x−y∣|x-y|xy。合理分配,使得总代价最少,并且满足顺时针第iii个点上有rir_iri头牛。

数据

输入
6 2
2
5
4
2
6
2
输出
14

解释

把管辖点放在2,52,52,5号点上,然后如图所示分配。
blog1.jpg

思路

很明显DPDPDP。由于是顺时针,所以我们是向后考虑。dp[i][kk]dp[i][kk]dp[i][kk]表示到点iii,放kkkkkk(为了防止和给定的kkk重名)个源,的最小代价。如何断环成链。。。只要每次初始化一下DPDPDP数组,然后用rotate()rotate()rotate()函数把第一个放到最后面,循环nnn次,就珂以了。

然后一个显然的事实:因为要求最小,所以初始值要设置成0x3f0x3f0x3f,但是dp[n+1][0]=0dp[n+1][0]=0dp[n+1][0]=0,这个边界别忘了。(忘了样例都过不去)。dp[i][kk]dp[i][kk]dp[i][kk]的时候,只要去前面找一个后继节点p(p&gt;i)p(p&gt;i)p(p>i),表示我们在ppp这个位置放了一个源,然后只要暴力统计距离和,然后用dp[p][kk−1]+sumdp[p][kk-1]+sumdp[p][kk1]+sum(就是刚刚求出来的那个距离和) 的值更新dp[i][kk]dp[i][kk]dp[i][kk]的值。然后只要求每次滚动时dp[1][k]dp[1][k]dp[1][k]的最小值即可。

这个是O(n3k)O(n^3k)O(n3k)的,对于本题足够了。但是也有O(n2k)O(n^2k)O(n2k)的斜率优化做法,我不会

代码:

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f3f3f3f3fll
#define int long long
#define N 110
using namespace std;

int n,k;
int r[N];

void Input()
{
    scanf("%lld%lld",&n,&k);
    for(int i=1;i<=n;++i)
    {
        scanf("%lld",&r[i]);
    }
}

int dp[N][8];
void upmin(int &x,int y)//用y更新x的最小值
{
    x=min(x,y);
}
void DP()
{
    int ans=INF;
    for(int i=1;i<=n;++i)
    {
        memset(dp,0x3f,sizeof(dp));
        dp[n+1][0]=0;//边界别忘了

        for(int kk=1;kk<=k;++kk)
        {
            for(int j=1;j<=n;++j)
            {
                int sum=0;
                for(int p=j+1;p<=n+1;++p)
                {
                    sum+=r[p-1]*(p-j-1);
                    upmin(dp[j][kk],dp[p][kk-1]+sum);//状态转移方程
                }
            }
        }
        upmin(ans,dp[1][k]);//ans取dp[1][k]中的最小值
        rotate(r+1,r+2,r+n+1);
    }
    printf("%lld\n",ans);
}

main()
{
    if (0)
    {
        freopen("cbarn2.in","r",stdin);
        freopen("cbarn2.out","w",stdout);
    }
    Input();
    DP();
    return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值