题解:P13404 [GCJ 2010 #3] Fence
_MiyazonoKaori_ · · 题解
一道非常好的图论,主要是如何转化为最短路。
思路:
拿到题第一眼认为是一个 DP,但
通过我们惊人的注意力可以发现一个非常简单的贪心:
- 需要取尽可能多的长度尽可能大的木板才能找到最优解。
令
我们肯定希望
但这只是理想情况,实际上
那么下面就是图论建模:
我们把余数看作图的节点:
- 节点集合:
\{0,1,2,\dots,A-1\} ; - 每次选择一块木板长度
b ,余数变化为:r \to (r+b) \bmod A
为了保证最少木板数,我们定义边权:
- 每条边权重为
A-b 。
现在这就是一个裸的最短路,通过 Dijkstra 可以求出,加堆优化的话时间复杂度近似
CODE:
const ll INF=(1LL<<62);
int main() {
int T=read();
for(int TTT=1;TTT<=T;TTT++){
cout<<"Case #"<<TTT<<": ";
ll L;
int n;cin>>L>>n;
vector<int>a(n);
for(int i=0;i<n;i++){
cin>>a[i];
}
int A=*max_element(a.begin(),a.end());
ll p=L/A,q=L%A;
vector<ll>w(A,INF);
for(int i=0;i<n;i++){
ll del=a[i]%A;
ll wn=A-a[i];
if(wn<w[del]) w[del]=wn;
}
bool flag=false;
for(int i=0;i<n;i++){
if(w[i]!=INF){
flag=true;
break;
}
}
if(!flag){
if(q==0) cout<<p<<endl;
else cout<<"IMPOSSIBLE\n";
continue;
}
vector<ll>dist(A,INF);
dist[0]=0;
#define PIIL pair<ll,int>
priority_queue<PIIL,vector<PIIL>,greater<PIIL>>pq;
pq.push({0,0});
while(!pq.empty()){
ll d=pq.top().first;
int u=pq.top().second;
pq.pop();
if(d!=dist[u]) continue;
for(int i=0;i<A;i++){
ll wn=w[i];
if(wn==INF) continue;
int v=u+i;
if(v>=A) v-=A;
ll nd=d+wn;
if(nd<dist[v]){
dist[v]=nd;
pq.push({nd,v});
}
}
}
if(dist[q]==INF){
cout<<"IMPOSSIBLE\n";
continue;
}
cout<<p+(dist[q]+q)/A<<endl;
}
return 0;
}
在没有卡常的情况下用时 762ms。
那么还是要优化的:
- 显然令
g=\gcd(B_1,B_2,\dots,B_n) ,若g\nmid L ,那么必定无解; - 模空间压缩:只考虑余数图中能到达的节点。令
G=\gcd(A,\delta_1,\dots,\delta_k) ,其中\delta_i=B_i\bmod A 。仅能到达余数为q 且满足q\bmod G=0 的节点;并可将模空间从大小A 压缩为A' = A/G ,将所有\delta 与目标q 同除以G ; - 对于小数据可以使用 SPFA,但别忘了大数据它会退化;
- Bellman-Ford 也一样;
对于希望抢最优解的非常有用。