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

UVA1377

时间:2016-05-07 10:52:25      阅读:146      评论:0      收藏:0      [点我收藏+]

标签:

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


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用的 选了某个数 他所增加的可以量的长度 必须是没存在过的 他才有用

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

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

UVA1377

标签:

原文地址:http://blog.csdn.net/kevin66654/article/details/51334244

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