标签:cstring || 状态 不同的 continue als block ++ int
给定 \(n\) 个长度,求构造刻度尺上最小的 \(m\) 个刻度使之能量出这 \(n\) 个长度。
题目保证 \(m\leq 7\)。
一开始眼瞎没看到最后那个条件,导致一度认为此题不可做。
看上去没有什么好的方法,贪心又两秒钟被叉掉了,数据范围看上去又很小,这种题搜索没跑了。
求使用的刻度数量尽量小,显然用 bfs 更加合适。
下面先引出几条引理及其证明:
引理 1:
要求在 \(m\) 尽可能小的前提下尺子总长度尽量短,尺子的长度一定等于 \(n\) 个长度中的最大值。
设尺子长度为 \(L\),最大的 \(d\) 为 \(d_0\)。
显然 \(L\geq d_0\),否则 \(d_0\) 无法量出。
不难发现 \(L\leq d_0\),否则可以通过将 \(>d_0\) 的刻度转移到 \(0\sim d_0\) 中来获得更优的答案。
所以 \(L=d_0\)。
引理 2:
不同的长度最多有 \(21\) 种。
因为 \(m\leq 7\)。
\(7\) 个刻度能产生的不同间距最多为 \(C_7^2=21\)。
引理 \(1\) 可以很好的限定范围,引理 \(2\) 保证了时间复杂度。
然后是搜索时的状态转移,由于引理 2,可以考虑直接状压 state
表示当前满足的长度。
然后还要记录当前选择的刻度,为了方便这里使用 set,当然 vector 或 定长数组 都是可以的。
显然下一个刻度一定可以由 某个刻度 加上或减去 某个长度 得到,否则这个刻度没有任何用处。
所以直接 加 / 减 转移即可,重复的 state
显然不需要再次入队,利用数组判重即可。
最坏时间复杂度是 \(O(n2^n)\),其中 \(n\) 是去重后的长度总数,实际远小于这个值。
参考代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>
#include<queue>
using namespace std;
const int N = 1000010;
const int M = 2100000;
int n, mx, d[55], id[N];
bool vis[M];
vector<int> ans;
struct node{int state; set<int> st;};
#define IT set<int>::iterator it
int read(){
int x = 0, f = 1; char c = getchar();
while(c < ‘0‘ || c > ‘9‘) f = (c == ‘-‘) ? -1 : 1, c = getchar();
while(c >= ‘0‘ && c <= ‘9‘) x = x * 10 + c - 48, c = getchar();
return x * f;
}
void bfs(){
queue<node> q;
node s;
s.state = 0, s.st.insert(0);
q.push(s);
while(!q.empty()){
node u = q.front(); q.pop();
if(u.state == (1 << n) - 1){
for(IT = u.st.begin(); it != u.st.end(); it ++)
ans.push_back(*it);
return;
}
if(u.st.size() == 7) continue;
// plus
for(int i = 0; i < n; i ++) if(!(u.state >> i & 1)){
for(IT = u.st.begin(); it != u.st.end(); it ++) if(*it + d[i] <= mx){
int now = *it + d[i];
node tmp = u;
for(IT = tmp.st.begin(); it != tmp.st.end(); it ++){
int dis = abs(*it - now);
if(id[dis] == -1) continue;
tmp.state |= (1 << id[dis]);
}
if(vis[tmp.state]) continue;
vis[tmp.state] = true;
tmp.st.insert(now);
q.push(tmp);
}
}
// minus
for(int i = 0; i < n; i ++) if(!(u.state >> i & 1)){
for(IT = u.st.begin(); it != u.st.end(); it ++) if(*it - d[i] > 0){
int now = *it - d[i];
node tmp = u;
for(IT = tmp.st.begin(); it != tmp.st.end(); it ++){
int dis = abs(*it - now);
if(id[dis] == -1) continue;
tmp.state |= (1 << id[dis]);
}
if(vis[tmp.state]) continue;
vis[tmp.state] = true;
tmp.st.insert(now);
q.push(tmp);
}
}
}
}
int main(){
int C = 0;
while(n = read()){
for(int i = 0; i < n; i ++) d[i] = read();
sort(d, d + n);
n = unique(d, d + n) - d;
memset(id, -1, sizeof(id));
memset(vis, false, sizeof(vis));
mx = d[n - 1];
for(int i = 0; i < n; i ++) id[d[i]] = i;
ans.clear();
bfs();
int sz = ans.size();
printf("Case %d:\n%d\n", ++ C, sz);
for(int i = 0; i < sz; i ++)
if(i == sz - 1) printf("%d\n", ans[i]);
else printf("%d ", ans[i]);
}
return 0;
}
标签:cstring || 状态 不同的 continue als block ++ int
原文地址:https://www.cnblogs.com/lpf-666/p/14687644.html