读完题意就知道是一个暴力乱搞题,但是苦于水平有限,只能赛后补
题意:n个长度值,需要提供一把刻度尺,尺子上的刻度越少越好,尺子越短越好,要求是:必须有0刻度,n个长度值可以直接测量
注意hint的提示:最多就是7个!
我自己想的是二进制枚举:
把0这个长度值放入n个长度值中
任意一个状态用一个整数表示,然后去判断,选择覆盖了所有长度值得方案
因为i值是从小到大的,所以只要对n个长度值排序去重,就可以保证满足刻度少且长度短的限制条件
这种方法都是错的!因为有可能出现这种数据11 21 31 41 51 61 71 81
然后答案应该是0 10 21 41 61 81,即可以用大的和差值去构造小的
同理,也可以用加法构造
意味着,可以出现这n个长度值之外的数
for(int i=0;i<(1<<n);i++)
if (ok(i)){
getans(i);
break;
}
但是水平有限不会判重和对答案的记录。不然跟dfs没有区别的
暴力bfs或者dfs都可以,提供两种不同的姿势
bfs来源:http://blog.youkuaiyun.com/accelerator_/article/details/18312437
bfs想法:每添加一条边,就和现有的所有状态来判断,是不是因为这条边的加入,使得可以创造出n个长度中的某几个,然后状态压缩放进去,新状态继续放入队列。当队列中有元素满足时候,记得最优值;当队列为空的时候,就可以退出
#include<map>
#include<set>
#include<math.h>
#include<time.h>
#include<iostream>
#include<cstdio>
#include<queue>
#include<stack>
#include<stdio.h>
#include<cstring>
#include<string.h>
#include<algorithm>
#include<cstdlib>
using namespace std;
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
#define ll rt<<1
#define rr rt<<1|1
#define LL long long
#define ULL unsigned long long
#define maxn 1050
#define maxnum 1000050
#define eps 1e-6
#define input freopen("input.txt","r",stdin)
#define output freopen("output.txt","w",stdout)
int N,n,id[maxnum],d[25],cas=0,Max,vis[maxnum<<1];
set<int> ans;
struct S{
int state;
set<int> ans;
}p;
void init(){
int D;
n=0;
ans.clear();
memset(id,0,sizeof(id));
memset(vis,0,sizeof(vis));
for(int i=0;i<N;i++){
scanf("%d",&D);
if (!id[D]){
d[n++]=D;
id[D]=1;
}
}
sort(d,d+n);
Max=d[n-1];
memset(id,-1,sizeof(id));
for(int i=0;i<n;i++) id[d[i]]=i;
//for(int i=0;i<n;i++) printf("%d%c",id[d[i]],i==n-1?'\n':' ');
//id[d[i]是给每条边编号,方便二进制枚举时候的一一对应
//for(int i=0;i<n;i++) printf("%d%c",d[i],i==n-1?'\n':' ');
//这个值就是序号值
}
void BFS(){
queue<S> Q;
p.ans.clear();
p.ans.insert(0);
p.state=0;
//初始化节点
Q.push(p);
while(!Q.empty()){
p=Q.front();
Q.pop();
if (p.state==(1<<n)-1){
//判断是不是达到终点了,所有的边都出现过
if (ans.size()==0) ans=p.ans;
else{
if (ans.size()<p.ans.size()) return;
else if (ans.size()>p.ans.size()) ans=p.ans;
else if (*ans.rbegin()>*p.ans.rbegin()) ans=p.ans;
}
//现有解和当前最优解取较优解
}
if (p.ans.size()==7) continue;
//达到最大数量的剪枝,不需要继续计算
for(int i=0;i<n;i++){
if ((p.state)&(1<<i)) continue;
//如果当前这条边出现过,就没有意义 下面用减法或者加法来得到d[i]的长度
for(set<int>::iterator it=p.ans.begin();it!=p.ans.end();it++){
if (*it>d[i]){//如果可以利用减法,往前看算出长度
S q=p;
int sum=*it-d[i];
for(set<int>::iterator it2=q.ans.begin();it2!=q.ans.end();it2++){
int nu=abs(*it2-sum);
if (id[nu]==-1) continue;
q.state=(q.state|(1<<id[nu]));
}
q.ans.insert(sum);
if (!vis[q.state]){
Q.push(q);
vis[q.state]=1;
}
}
if (*it+d[i]<=Max){//如果可以利用加法,往后看算出长度
S q=p;
int sum=*it+d[i];
for(set<int>::iterator it2=q.ans.begin();it2!=q.ans.end();it2++){
int nu=abs(*it2-sum);
if (id[nu]==-1) continue;
q.state=(q.state|(1<<id[nu]));
}
q.ans.insert(sum);
if (!vis[q.state]){
Q.push(q);
vis[q.state]=1;
}
}
}
}
}
}
void solve(){
BFS();
int bo=0;
printf("Case %d:\n%d\n",++cas,ans.size());
for(set<int>::iterator it=ans.begin();it!=ans.end();it++){
if (bo++) printf(" ");
printf("%d",*it);
}
puts("");
}
int main(){
//input;
while(scanf("%d",&N)!=EOF){
if (!N) break;
init();
solve();
}
return 0;
}
dfs的方法是另外一位聚聚,弱还没太懂,上链接吧
http://www.cnblogs.com/GeniusYang/p/5460642.html
解释:
挑7个以内的刻度 这些刻度能把目的长度都表示就满足条件
每挑一个刻度 他就能和前面所有的刻度 组成新的长度 这些长度有的是以前的刻度组合不出来的 有的是可以的
tar是 所有可用的长度 初始的所有可用长度就是 那n个数,他们的差也是可用的长度
然后flag负责不让重复
fla1是dfs用的 选了某个数 他所增加的可以量的长度 必须是没存在过的 他才有用
选了他 加上 然后回溯的时候减去就好了
学习了这些巨巨的代码,非常感谢