洛谷:牛场围栏(同余类BFS)

题目背景

小L通过泥萌的帮助,成功解决了二叉树的修改问题,并因此写了一篇论文,

成功报送了叉院(羡慕不?)。勤奋又勤思的他在研究生时期成功转系,考入了北京大学光华管理学院!毕业后,凭着自己积累下的浓厚经济学与计算机学的基础,成功建设了一个现代化奶牛场!

题目描述

奶牛们十分聪明,于是在牛场建围栏时打算和小L斗智斗勇!小L有N种可以建造围栏的木料,长度分别是l1,l2 … lN,每种长度的木料无限。

修建时,他将把所有选中的木料拼接在一起,因此围栏的长度就是他使用的木料长度之和。但是聪明的小L很快发现很多长度都是不能由这些木料长度相加得到的,于是决定在必要的时候把这些木料砍掉一部分以后再使用。

不过由于小L比较节约,他给自己规定:任何一根木料最多只能削短M米。当然,每根木料削去的木料长度不需要都一样。不过由于测量工具太原始,小L只能准确的削去整数米的木料,因此,如果他有两种长度分别是7和11的木料,每根最多只能砍掉1米,那么实际上就有4种可以使用的木料长度,分别是6, 7,10, 11。        

因为小L相信自己的奶牛举世无双,于是让他们自己设计围栏。奶牛们不愿意自己和同伴在游戏时受到围栏的限制,于是想刁难一下小L,希望小L的木料无论经过怎样的加工,长度之和都不可能得到他们设计的围栏总长度。不过小L知道,如果围栏的长度太小,小L很快就能发现它是不能修建好的。因此她希望得到你的帮助,找出无法修建的最大围栏长度。

这一定难不倒聪明的你吧!如果你能帮小L解决这个问题,也许他会把最后的资产分给你1/8哦!

输入输出格式

输入格式:

 

输入的第一行包含两个整数N,  M,分别表示木料的种类和每根木料削去的最大值。以下各行每行一个整数li(1< li< 3000),表示第i根木料的原始长度。

 

输出格式:

 

输出仅一行,包含一个整数,表示不能修建的最大围栏长度。如果任何长度的围栏都可以修建或者这个最大值不存在,输出-1。

 

输入输出样例

输入样例#1: 复制

2 1
7 11

输出样例#1: 复制

15

说明

40 % :1< N< 10,  0< M< 300

100 % :1< N< 100,  0< M< 3000 

题意:给无限根给定长度的若干种木棒,问不能组成的最大数。

思路:同余类bfs问题,取最短的木棒作为周期,设它的长度为a,把数轴分成无限个a,对a取模相等的下标作为同一类,比如a为3时,4、7、10、13......是同一类,记为1(因为mod 3=1),那么建图跑最短路后dis[1]的值就是4、7、10、13......的其中一个,也就是这一类点集的第一个可到达的点,因为后面的点都可以通过加a到达。那么这题就好做了,显然dis[x]-a就是x同余类的最大不可达点,判断-1也是简单,某个点不可达表示此同余类一个都不能到达,所有点距离为0(a为1)表示全部点都可达。

# include <bits/stdc++.h>
using namespace std;
const int maxn = 4e5+30;
int a[maxn], in[maxn], dis[maxn];
vector<pair<int,int> >g[maxn];
queue<int>q;
int main(){
    int n, m, x, cnt=0;
    scanf("%d%d",&n,&m);
    for(int i=0; i<n; ++i){
        scanf("%d",&x);
        for(int i=0; i<=m && x-i>0; ++i) a[cnt++] = x-i;
    }

    memset(dis, 0x3f, sizeof(dis));
    sort(a, a+cnt);
    cnt = unique(a, a+cnt)-a;
    for(int i=0; i<a[0]; ++i)
        for(int j=1; j<cnt; ++j)
            g[i].push_back({(i+a[j])%a[0], a[j]});
    q.push(0);
    dis[0] = 0;
    while(!q.empty()){
        int u = q.front();
        q.pop();
        in[u] = 0;
        for(int i=0; i<g[u].size(); ++i){
            int v = g[u][i].first;
            int w = g[u][i].second;
            if(dis[v] > dis[u] + w){
                dis[v] = dis[u] + w;
                if(!in[v]){
                    q.push(v);
                    in[v] = 1;
                }
            }
        }
    }
    int ans = 0;
    for(int i=0; i<a[0]; ++i)
        ans = max(ans, dis[i]-a[0]);
    if(ans == 0 || ans == 0x3f3f3f3f-a[0]) puts("-1");
    else printf("%d\n",ans);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值