洛谷 P2662 牛场围栏 (同余最短路)
题目:传送门
题意
小L有N种可以建造围栏的木料,长度分别是l1,l2 … lN,每种长度的木料无限。任何一根木料最多只能削短M米,修建时,他将把所有选中的木料拼接在一起,因此围栏的长度就是他使用的木料长度之和。问不能修建的围栏长度最大是多少,若任何长度的围栏都能修建,就输出 -1,或者不能修建的这个最大值不存在(也就是无穷大)也输出 -1.
1 < N < 100, 0 < M < 3000
思路
考虑输出 -1 的情况:
①:若某根木棒长度等于 1 或者削短后等于 1,那么所有的长度都能被表示出来,那就输出 -1.
②:若 gcd({l1, l1 - 1,.... l1 - m, l2,..... l2 - m, … lN, ..... lN - m}) > 1,也就是所有数的 gcd 都大于 1,那么不能被表示的是无穷大的,我们设 q 为所有数的 gcd,那么任意 % q != 0 的长度都不能被修建,显然这个数是无穷大的。
剩下的就是有解的情况。
我们设 dis[i] 表示由 n 种木棒和他们削短 1 ~ m 之后的木棒中选定一些能凑出来的 % (min({l1, l2, l3 .. lN}-m) 等于 i 的最小的数,那最大不能建成的就是 max{dis[i] - ( min(l1,l2..lN) - m ) } 。
#include <bits/stdc++.h> #define LL long long #define ULL unsigned long long #define mem(i, j) memset(i, j, sizeof(i)) #define rep(i, j, k) for(int i = j; i <= k; i++) #define dep(i, j, k) for(int i = k; i >= j; i--) #define pb push_back #define make make_pair #define INF INT_MAX #define inf LLONG_MAX #define PI acos(-1) #define fir first #define sec second using namespace std; const int N = 2500010; const LL mod = 1e9 + 7; int n, m, a[500], dis[N << 1], vis[N << 1], tot; int fir[N], nx[N << 1], to[N << 1], cost[N << 1]; void add(int u, int v, int w) { nx[++tot] = fir[u]; fir[u] = tot; to[tot] = v; cost[tot] = w; } void spfa() { queue < int > q; mem(dis, 0x3f3f3f3f); mem(vis, 0); vis[0] = 1; dis[0] = 0; q.push(0); while(!q.empty()) { int u = q.front(); q.pop(); vis[u] = 0; for(int e = fir[u]; e; e = nx[e]) { int v = to[e], w = cost[e]; if(dis[v] > dis[u] + w) { dis[v] = dis[u] + w; if(!vis[v]) { q.push(v); vis[v] = 1; } } } } } int main() { scanf("%d %d", &n, &m); rep(i, 1, n) scanf("%d", &a[i]); sort(a + 1, a + 1 + n); if(a[1] - m <= 1) { puts("-1"); return 0; } rep(i, 1, n) { rep(j, max(a[i] - m, a[i - 1] + 1), a[i]) { rep(k, 0, a[1] - m - 1) { add(k, (k + j) % (a[1] - m), j); } } } spfa(); int ans = 0; rep(i, 1, a[1] - m - 1) { if(dis[i] > 100000000) { puts("-1"); return 0; } ans = max(ans, dis[i] - (a[1] - m)); } printf("%d\n", ans); return 0; }
一步一步,永不停息