[题解]P13404 [GCJ 2010 #3] Fence

给一个能比较自然想到的思考方向。

思路

从直觉上来说,记 \(m = \max\{a_i\}\),我们的决策显然是需要用较多的 \(m\),不妨设 \(L = k \times m + r\),其中 \(k\) 表示 \(m\) 使用的数量,\(r\) 表示用除 \(m\) 以外的木板凑出的长度。

这里我们设 \(t\) 表示凑出长度为 \(r\) 最少用 \(t\) 块木板,那么有需要用的总木板为 \(t + \frac{L - r}{m} = \frac{m \times t + L - r}{m}\),其中 \(m,L\) 都是常数,现在我们的目标就是最小化 \(m \times t - r\)

接下来肯定要寻找一些关于 \(t,r\) 的限制。注意到唯一的限制是 \(L \equiv r \pmod m\),这启示我们从 \(r \bmod m\) 的角度入手。定义 \(dp_i\) 表示当 \(r \bmod m = i\)\(m \times t - r\) 的最小值,显然有 \(dp_0 = 0\)。考虑转移,枚举当前使用的木板 \(a_j\)

\[dp_i + (m - a_j) \rightarrow dp_{(i + a_j) \bmod m} \]

由于转移可能有环状结构,不妨直接把转移丢到图上考虑,即 \(i \to (i + a_j) \bmod m\) 之间有一条边权为 \(m - a_j\) 的有向边,最后 \(dp_{L \bmod m}\) 显然可以表示为 \(0 \leadsto L \bmod m\) 的最短路。代回原式,答案为 \(\frac{L + dp_{L \bmod m}}{m}\)

Code

#include <bits/stdc++.h>
#define re register
#define fst first
#define snd second
#define int long long
#define chmax(a,b) (a = max(a,b))

using namespace std;

typedef pair<int,int> pii;
const int N = 1e5 + 10;
const int inf = (int)(1e18) + 10;
int l,n,m;
int arr[N],dis[N];
bool vis[N];

inline int read(){
    int r = 0,w = 1;
    char c = getchar();
    while (c < '0' || c > '9'){
        if (c == '-') w = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9'){
        r = (r << 3) + (r << 1) + (c ^ 48);
        c = getchar();
    }
    return r * w;
}

inline void solve(int tid){
    l = read(),n = read(),m = 0;
    for (re int i = 1;i <= n;i++) chmax(m,arr[i] = read());
    fill(dis,dis + m + 3,inf);
    fill(vis,vis + m + 3,false);
    priority_queue<pii,vector<pii>,greater<pii>> pq;
    pq.push({dis[0] = 0,0});
    while (!pq.empty()){
        pii u = pq.top(); pq.pop();
        if (vis[u.snd]) continue;
        vis[u.snd] = true;
        for (re int i = 1;i <= n;i++){
            int v = (u.snd + arr[i]) % m,w = (m - arr[i]);
            if (dis[v] > dis[u.snd] + w) pq.push({dis[v] = dis[u.snd] + w,v});
        }
    } int ans = dis[l % m];
    printf("Case #%lld: ",tid);
    if (ans >= inf) puts("IMPOSSIBLE");
    else printf("%lld\n",(l + ans) / m);
}

signed main(){
    int T = read();
    for (re int i = 1;i <= T;i++) solve(i);
    return 0;
}
posted @ 2026-02-05 15:34  WBIKPS  阅读(2)  评论(0)    收藏  举报