码迷,mamicode.com
首页 > 其他好文 > 详细

UVA1377 Ruler

时间:2021-04-22 16:21:22      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:cstring   ||   状态   不同的   continue   als   block   ++   int   

Ruler

给定 \(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;
}

UVA1377 Ruler

标签:cstring   ||   状态   不同的   continue   als   block   ++   int   

原文地址:https://www.cnblogs.com/lpf-666/p/14687644.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!