vijos[P1054] luogu[P2662] 牛场围栏 (数论+最短路,(DP可水过))

来源:互联网 发布:复制别人的淘宝店铺 编辑:程序博客网 时间:2024/05/09 11:22
描述John计划为他的牛场建一个围栏,以限制奶牛们的活动。他有N种可以建造围栏的木料,长度分别是l1,l2…lN,每种长度的木料无限。修建时,他将把所有选中的木料拼接在一起,因此围栏的长度就是他使用的木料长度之和。但是聪明的John很快发现很多长度都是不能由这些木料长度相加得到的,于是决定在必要的时候把这些木料砍掉一部分以后再使用。不过由于John比较节约,他给自己规定:任何一根木料最多只能削短M米。当然,每根木料削去的木料长度不需要都一样。不过由于测量工具太原始,John只能准确的削去整数米的木料,因此,如果他有两种长度分别是7和11的木料,每根最多只能砍掉1米,那么实际上就有4种可以使用的木料长度,分别是6, 7, 10, 11。Clevow是John的牛场中的最聪明的奶牛,John请她来设计围栏。Clevow不愿意自己和同伴在游戏时受到围栏的限制,于是想刁难一下John,希望John的木料无论经过怎样的加工,长度之和都不可能得到她设计的围栏总长度。不过Clevow知道,如果围栏的长度太小,John很快就能发现它是不能修建好的。因此她希望得到你的帮助,找出无法修建的最大围栏长度。

输入格式输入的第一行包含两个整数N, M (1<N<100, 0<=M<3000),分别表示木料的种类和每根木料削去的最大值。以下各行每行一个整数li(1<li<3000),表示第i根木料的原始长度。输出格式输出仅一行,包含一个整数,表示不能修建的最大围栏长度。如果任何长度的围栏都可以修建或者这个最大值不存在,输出-1。样例1样例输入1[复制]2 17 11样例输出1[复制]15限制各个测试点1秒来源WinterCamp 2002

DP水过(luogu上DP可水过, vijos上会WA)

可行性dp,f[j]表示组成j长度是否可行,只要任意j-任意一根可生成的木棒长度可行就可以了,而目标是找到第一根不可行的,输出即可,注意预处理的时候判爆负数      ————luogu题解

代码:

#include <cstdio>#include <iostream>#include <string>#include <algorithm>using namespace std;int n, m, vis[1200000], f[2211111];int c[9211111];int main() {    cin>>n>>m;    for(int i = 1; i <= n; i++) {        int x;        scanf("%d", &x);        for(int j = 0; j <= m && x - j; j++) {            if(!vis[x - j]) {                vis[x - j] = 1;                c[++c[0]] = x - j;                f[x - j] = 1;                if(x - j == 1) {                    cout<< - 1 <<endl;                    return 0;                }            }        }    }    for(int i = 1; i <= c[0]; i++) {        int now = c[i];        for(int j = 0; j <= 100010; j++) {            if(f[j - c[i]]) f[j] = 1;        }    }        for(int j = 100010; j >= 0; j--) {        if(!f[j]) {            cout<<j<<endl;            return 0;        }    }}

正解最短路

  • 首先把一开始能弄出的木棍长度处理出来
  • 找一个最小的, 设为min_length。 若最小的为1,则输出-1;
  • 然后抽象的把0 - min_length - 1 的每一个数看成一个点,某些长度%min_length后,一定在这之间。
  • 可以发现,如果我们能够组成某个长度x, 且 y = x % min_length; 又设X = y + i * min_length,则我们一定能够组成所有的X 且X满足下列关系(X%min_length == y);
  • 如果不能组成呢?那么我们一定不能组成任意一个X使得(X%min_length == y); 也就是说,不能组成的最大长度是无限大,此时无解,输出-1。
  • 如果能组成,我们只保存一个最小的x(x一定大于min_length),那么x - min_length 一定不能被组成。
  • 如果余数为 0 - min_length - 1的长度 都能组成,则找一个最大的x ,则x - min_length 为最大的不能组成的长度。
  • 所以,抽象出min_length - 1 个点,用dis[i] 表示余数为i的最小长度x。根据一开始的木棍长度,首先得到dis[i]的初始值,然后边走边建边。跑spfa即可
  • 这个建边过程是边跑spfa边建边,且不用存每个点可连向那些点,因为对于可以组成的i,其他的每个点都可以与i相连,即这边是它们共有的。
  • 为什么要边跑spfa边建边呢?因为对当前i,有一个j,(dis[i]+dis[j])%min_length == X 且dis[X]并未被更新,为初始极大INF,则建的边表中肯定不含这个点! 这时候把这个点加入表中。
    代码:
#include <cstdio>#include <iostream>#include <string>#include <algorithm>#include <queue>#include <vector>using namespace std;#define debug cout<<"Orzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"<<endl;const int INF = 2047483647;int dis[3426];int c[422567], vis[5122567];int n,m;queue <int> q;vector <int> v;int min_length = INF;int max_length = -5;int iv[3456];int spfa() {    for(int i = 0; i <= min_length; i++) {        dis[i] = INF;    }    for(int i = 1; i <= c[0]; i++) {//      cout<<now<<' '<<i<<' '<<c[i]<<' '<<endl;        int now = c[i] % min_length;        if(dis[now] == INF || dis[now] > c[i]) {            dis[now] = c[i];            q.push(now);        }    }//  debug;    while(!q.empty()) {        int now = q.front(); q.pop();        vis[now] = 0;        if(!iv[now]) {            v.push_back(now);            iv[now] = 1;        }        for(int i = 0; i < v.size(); i++) {            int nn = (dis[v[i]] + dis[now]) % min_length;            if(dis[nn] > dis[now] + dis[v[i]]) {                dis[nn] = dis[now] + dis[v[i]];//              if(nn_mod == 3) {//                  cout<<dis[nn_mod] <<' '<<"dis["<<now_mod<<"] = "<<dis[now_mod] << ' '<<nn<<endl;//              }                if(!vis[nn]) {                    vis[nn] = 1;                    q.push(nn);                }            }        }    }    for(int i = 0; i < min_length; i++) {//      cout<<i<<' '<<dis[i]<<' '<<min_length<<' '<<endl;        if(dis[i] < INF) {            max_length = max(max_length, dis[i]);        } else return -1;    }    return max_length - min_length;}int main() {    cin>>n>>m;    for(int i = 1; i <= n; i++) {        int x;        scanf("%d", &x);        for(int j = 0; j <= m && x - j; j++)             if(!vis[x - j]) {                vis[x - j] = 1;                c[++c[0]] = x - j;                min_length = min(min_length , c[c[0]]);                if(x - j == 1) {                    cout<< - 1 <<endl;                    return 0;                }            }    }    cout<<spfa()<<endl;//  cout<<spfa();    return 0;}
1 0
原创粉丝点击