BestCoder Round #83 zxa and wifi(一个奇怪的DP题)

5 篇文章 0 订阅
4 篇文章 0 订阅

大大的打个脸,这个题这样的解法是错误的,不过思路还是挺妙的,可以看看吧。

听说BC要停办了,莫名的有点悲伤,晚上停办前最后一场,遇上了一个奇怪的DP题,通过率好低,特地裱起来自己做纪念。。。。(ˉ﹃ˉ),

题目描述

zxa来到Q镇做义工,镇长希望给住在Q镇中轴线上的nn户人家实现网络覆盖。这nn户人家可以看作是中轴线上的质点,从东到西依次编号从 1 n,其中第 i(1i<n) 户人家到第 (i+1) 户人家的距离为 di ​​ 。

zxa负责了这个项目的方案设计,他获悉运营商给出了两种架设网络的方式。一种是花费 ai 的费用在第 i(1in) 户人家安装无线路由器和相关网线使得距离第ii户人家不超过 ri 的人家(包括第ii户人家)都能上网。另一种是花费 bi 的费用为第 i(1in) 户人家接通光缆使得该户人家能上网。

zxa很好奇,如果镇上为了防止无线电的辐射过大而至多只能在 k 户人家架设无线路由,那么使得这n户人家都能上网的最小花费是多少,你能帮助他吗?

输入

这里写图片描述
原谅我传了图,数学公式乱码了,大家可以点这里去原题

输出

对于每组数据,输出一行,包含一个正整数,表示使得这nn户人家都能上网的最小花费。

样例

输入
输入样例

2
2 1
1
12 11 3
1 7 4
5 5
7 4 8 6
13 6 3
14 2 3
3 6 4
11 12 2
9 14 4

输出样例

1
12

Hint

对于第二组样例,zxa在33号人家安装无线路由器,在11号、44号、55号人家接通光缆,这样的总代价是3+3+2+4=123+3+2+4=12。

解题思路

这个题乍一看挺复杂,我先从模拟的角度去考虑发现过程中有决策,那就是dp了
考虑
dp[i][0]:该节点使用光纤连接,从1-i节点所花费用的最小值。
dp[i][1]:该节点使用wifi连接,从1-i节点所花费用的最小值。
dp[i][2]:该节点被wifi覆盖,从1-i节点所花费用的最小值。

那就有转移式

dp[i][0]=minn=02dp[i1][n]

dp[i][1]=minn=02dp[j][n],jwifi

最特殊的是dp[i][2],这个数组是根据dp[j][1]来更新的,当j节点的wifi覆盖到i节点时,则 dp[i][2]=min(dp[i][2],dp[j][1]) ,当然,dp[i][2]所有初始值要设成最大值,注意覆盖是两个方向的,同时要先计算出dp[j][1]

代码

#include <cstdio>
#include <algorithm>
using namespace std;
#define MAXN 100001
int a[MAXN],b[MAXN],d[MAXN],r[MAXN];
long long dp[10001][3];
int main(){
    //freopen("1.in","r",stdin);
    int tt,n,k;
    scanf("%d",&tt);
    while(tt--){
        scanf("%d%d",&n,&k);
        for(int i=1;i<n;++i){
            scanf("%d",&d[i]);
        }
        for(int i=1;i<=n;++i){
            scanf("%d%d%d",&a[i],&r[i],&b[i]);
        }
        for(int i=0;i<=n;i++){
            dp[i][2]=99999999999999999LL;
        }

        dp[0][0]=0;
        dp[0][1]=0;
        for(int i=1;i<=n;++i){
            //计算dp[i][0]
            dp[i][0]=min(min(dp[i-1][0],dp[i-1][1]),dp[i-1][2])+b[i];
            //向前找到当前节点i的wifi所不能覆盖到的前面节点
            int ri=r[i],j=i-1;
            while(ri>=d[j]&&j>=1){
                ri-=d[j--];
            }
            //计算dp[i][1]来计算当前
            dp[i][1]=min(min(dp[j][0],dp[j][1]),dp[j][2])+a[i];
            for(int l=j+1;l<i;l++){
                dp[l][2]=min(dp[l][2],dp[i][1]);
            }
            j=i+1,ri=r[i];
            while(ri>=d[j-1]&&j<=n){
                ri-=d[j-1];
                dp[j][2]=min(dp[j][2],dp[i][1]);
                j++;
            }
        }
        printf("%I64d\n",min(min(dp[n][0],dp[n][1]),dp[n][2]));
    }
    return 0;
}

好吧,写着写着博客,发现自己错了,没有考虑K,还是太天真。。。。Orz,这篇文章就先放着吧,看题解去。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值