专栏文章

[题解] P3052

P3052题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@minl7avo
此快照首次捕获于
2025/12/02 04:13
3 个月前
此快照最后确认于
2025/12/02 04:13
3 个月前
查看原文
竟然没有看到 dijkstra 的题解,写一发

主要思路

一看 NN 很小,考虑状压。
我们用 ff 表示某种选择牛的情形,第 ii 位上为 11 表示第 ii 只牛已经坐电梯下楼了/已经在电梯上了,为 00 表示还没有。
我们对于每个不同的 ff 看作不同的节点,则对于每个节点,我们记录:
  • wiw_i 表示最近一部电梯(还没下楼)里面的牛已经占了多少空间
  • disidis_i 表示达到当前情形已经送走了几部电梯(还没下楼的不算)
对于某个节点,如果我们当前情形是能通过 把一只牛送上电梯 这个操作来达到它对应的情形,那么就对它建边。即我们对所有一步可达的节点建边。
对于 dijkstra 的三角形不等式,我们也进行改造。 令 w1w1wu+ciw_u + c_i, d1d1disudis_u。如果 w1>Ww1 > W, 则将 w1w1 修改为 cic_i , 将 d1d1 增加一。即用 w1w1d1d1 表示把第 ii 只牛送上电梯后对应节点的 wwdisdis
对于如下条件:
  • disv>d1dis_v > d1
  • disv=d1dis_v = d1 并且 wv>w1w_v > w1
如果有任意一个满足,就对节点 vv 进行松弛操作。
最后输出 所有牛都上电梯的对应情形的 disfmaxdis_{f_{max}} +1+ 1 即可(最后一趟电梯也要算)。

对于部分证明的补充

  • dijkstra的贪心为什么是对的: 我们在队列优化时,依照 k=disiW+wik = dis_i * W + w_i 排序(由于 WW 一定大于等于 wiw_i,即以 disdis 为第一关键字,ww 为第二关键字排序)。
    • 我们注意到,kcik \ge \sum{c_i}
    • 我们发现,对于某个解 kik_i,如果存在另一个解 kjk_j ,使得 kikjk_i \ge k_j ,答案至少不会更劣(相等或 disdis 更小或 ww 更小)。
    • 显然没有负环
故dijkstra做法能够保障正确性,复杂度大致在 2nn22^n n^2

AC代码

CPP
#include <bits/stdc++.h>

#define N 300050
#define int long long
#define maxf ((1 << n) - 1)
#define inf 0x3f3f3f3f3f3f3f3f

using namespace std;

struct node{
	int v, w;
	bool operator < (const node &a) const {return w > a.w;}
};

int n, W;
int c[N], dis[N], w[N];
bool vis[N];

signed main(){
	cin >> n >> W;

	for(int i = 0ll; i < n; i++) cin >> c[i];

	memset(dis, 0x3f, sizeof dis); dis[0] = 0ll; w[0] = 0ll;
	priority_queue <node> q; q.push({0ll, 0ll});

	while(!q.empty()){
		int u = q.top().v; q.pop();
		if(vis[u]) continue; vis[u] = true;
		for(int i = 0; i < n; i++){
			if(u & (1ll << i)) continue;
			int v = u | (1ll << i);
			int d1 = dis[u], w1 = w[u] + c[i];
			if(w1 > W) d1++, w1 = c[i];
			if(dis[v] > d1 || (dis[v] == d1 && w[v] > w1)){
				dis[v] = d1; w[v] = w1;
				q.push({v, dis[v] * W + w[v]});
			}
		}
	}

	cout << dis[maxf] + bool(w[maxf]);

	return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...