P1220 关路灯 搜索剪枝
题目链接:
https://www.luogu.com.cn/problem/P1220
在搜索题单里第一次看到这种题目被吓住了
和之前我刷过的搜索题(全是板子)比,这里需要不断地朝不同方向搜索
并且让人汗流浃背的是1≤n≤50,爆搜肯定是炸了.
考虑这三个点
1.如何去遍历左右节点
2.记录状态的方案
3.怎么去优化节点搜索方案
1.遍历方法:
在每次搜索的时候选择左右两边遍历相邻顶点并且需要确保不越界
for(int i = 1;i<=n;++i){
//当前的位置
int _pos = (pos +- i);
//判断位置的合法性
_pos >= 1(向左)_pos <= n (向右)
//~~更新状态~~
}
2.记录方案方法:
记录当前功率,在搜索至下一个点的时候用记录的时间来更新当前电灯的耗电量.
3.足够强大的剪枝方案:
在dfs过程中记录结果,在关灯关到n个的情况下,对那些更劣解停止搜索
//_sum代表现在的电量总消耗,cost * _time只包括了现在消耗的所有灯的电量
//如果这里>= ans说明方法肯定更劣了
if(_sum + cost * _time >= ans)return;
做到这里,你只能得40分,因为还有一个点没优化
在遍历的过程中一旦确定了节点就说明当前节点没有实际用处了.
所以在dfs还原现场后退出for循环就行了
AC代码
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int inf = INT_MAX;
const int N = 55;
int T;
int n,k;
int ans = inf;
int a[N],v[N];
bitset<N>vis;
int sum = 0;
//现在的端点,关掉的灯的总数,走路时间,剩余的功率,耗电量
void dfs(int pos,int closed,int _time,int cost,int _sum){
if(_sum + cost * _time >= ans)return;
if(closed == n){
ans = min(ans,_sum);
return;
}
for(int i = 1;i<=n;++i){
if(i + pos <= n && !vis[i + pos]){
int gap = a[pos + i] - a[pos];
vis[i + pos] = 1;
dfs(pos + i,closed + 1,_time + gap,cost - v[i + pos],(_time + gap) * v[pos + i] + _sum);
vis[i + pos] = 0;
break;
}
}
for(int i = 1;i<=n;++i){
if(pos - i >= 1 && !vis[pos - i]){
int gap = a[pos] - a[pos - i];
vis[pos - i] = 1;
dfs(pos - i,closed + 1,_time + gap,cost - v[pos - i],(_time + gap) * v[pos - i] + _sum);
vis[pos - i] = 0;
break;
}
}
}
void solve(){
cin>>n>>k;
for(int i = 1;i<=n;++i){
cin>>a[i]>>v[i];
sum += v[i];
}
vis[k] = 1;
dfs(k,1,0,sum - v[k],0);
cout<<ans;
}
signed main() {
cin.tie(nullptr)->sync_with_stdio(0);
#ifdef LOCAL
freopen("in.in", "r", stdin);
freopen("out.out", "w", stdout);
#endif
T = 1;
while(T--){
solve();
}
return 0;
}

浙公网安备 33010602011771号