[题解]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;
}

浙公网安备 33010602011771号