UVA1377

来源:互联网 发布:数据挖掘师年薪 编辑:程序博客网 时间:2024/05/12 16:01

读完题意就知道是一个暴力乱搞题,但是苦于水平有限,只能赛后补


UVA1377

题意: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.csdn.net/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

解释:

就是 初始有n个长度 咱们的答案里可以用的刻度 是那n个长度和他们的差
挑7个以内的刻度 这些刻度能把目的长度都表示就满足条件
每挑一个刻度 他就能和前面所有的刻度 组成新的长度 这些长度有的是以前的刻度组合不出来的 有的是可以的

flag 所有可用长度的标记 是让存的时候不重复 fla是目的刻度的标记

tar是 所有可用的长度  初始的所有可用长度就是 那n个数,他们的差也是可用的长度

然后flag负责不让重复

fla1是dfs用的 选了某个数 他所增加的可以量的长度 必须是没存在过的 他才有用

选了他 加上 然后回溯的时候减去就好了

学习了这些巨巨的代码,非常感谢

0 0
原创粉丝点击