LA 3667 Ruler 两种不同形式的搜索来解决

这题目让我对bfs有更加深刻的认识了,好题一枚。

解法有两种,一种是递增暴搜,一种是常规的队列搜索

Uva 1377

题意:给定一些要测量的刻度,然后你要制造一把尺子,能测量这些,要求尺子的刻度数尽量少,并且尺子尽量短,题目保证刻度数不会大于7.

递增暴搜通过递增限制条件(刻度位置)

#include<iostream>  
#include<cmath>  
#include<cstdio>  
#include<sstream>  
#include<cstdlib>  
#include<string>  
#include<string.h>  
#include<cstring>  
#include<algorithm>  
#include<vector>  
#include<map>  
#include<set>  
#include<stack>  
#include<list>  
#include<queue>  
#include<ctime>  
#include<bitset>  
#include<cmath>  
#define eps 1e-6  
#define INF 0x3f3f3f3f  
#define PI acos(-1.0)  
#define ll __int64  
#define LL long long  
#define lson l,m,(rt<<1)  
#define rson m+1,r,(rt<<1)|1  
#define M 1000000007  
//#pragma comment(linker, "/STACK:1024000000,1024000000")  
using namespace std;  
  
#define Maxn 55  
#define Maxm 1100000  
int hav[Maxm],sa[Maxn],n,ans;  
bool vis[Maxn];  
int dis[Maxn];  
  
bool dfs(int cur)  
{  
    if(cur==ans)  
    {  
        for(int i=1;i<n;i++) //前n-1个长度都能够测量  
            if(!vis[i])  
                return false;  
        return true;  
    }  
    for(int i=1;i<cur;i++)  
    {  
        for(int j=1;j<n;j++)  
        {  
            if(!vis[j])  
            {  
                int dd=dis[i]+sa[j];//当前刻度  
                if(dd<=dis[cur-1]) //比之前大  
                    continue;  
                if(dd>=sa[n])//要比最大小  
                    continue;  
                dis[cur]=dd;  
  
                queue<int>myq; //记录标记的长度,回溯时返回  
                for(int k=1;k<cur;k++) //加入当前刻度后,新增的能够出的长度  
                {  
                    int temp=dis[cur]-dis[k];  
                    if(hav[temp]&&!vis[hav[temp]])  
                    {  
                        vis[hav[temp]]=true;  
                        myq.push(hav[temp]);  
                    }  
                }  
                int la=sa[n]-dis[cur]; //最后一段  
                if(hav[la]&&!vis[hav[la]])  
                {  
                    vis[hav[la]]=true;  
                    myq.push(hav[la]);  
                }  
                if(dfs(cur+1))  
                    return true;  
                while(!myq.empty())  
                {  
                    la=myq.front();  
                    myq.pop();  
                    vis[la]=false;  
                }  
            }  
        }  
    }  
    return false;  
}  
int main()  
{  
    //freopen("in.txt","r",stdin);  
   //freopen("out.txt","w",stdout);  
   int cas=0;  
  
   while(~scanf("%d",&n)&&n)  
   {  
       for(int i=1;i<=n;i++)  
            scanf("%d",&sa[i]);  
       sort(sa+1,sa+n+1);  
       n=unique(sa+1,sa+n+1)-sa-1;  
       memset(hav,0,sizeof(hav));  
       for(int i=1;i<=n;i++)  
            hav[sa[i]]=i;  
  
       dis[1]=0;  
       ans=2;  
       while(ans*(ans-1)/2<n)  
            ans++;  
  
       memset(vis,0,sizeof(vis));  
       while(!dfs(2))  
            ans++;  
  
       printf("Case %d:\n%d\n",++cas,ans);  
       printf("%d",dis[1]);  
       dis[ans]=sa[n];  
  
       for(int i=2;i<=ans;i++)  
          printf(" %d",dis[i]);  
       putchar('\n');  
  
   }  
    return 0;  
}  

第二种bfs通过将目标也就是状态放入队列的方法来进行搜索。

#include <iostream>  
#include <cstdio>  
#include <cstring>  
#include <algorithm>  
#include <set>  
#include <queue>  
using namespace std;  
#define MAX_SIZE    1000010  
//#define LOCAL  
struct Node  
{  
    set<int> st;  //存储当前可表示的刻度  
    int found;    //选取了哪些需要表示的刻度,二进制从小到大表示  
};  
char idx[MAX_SIZE];   //需要表示那些具体刻度idx[v]即刻度v是否是需要的刻度  
set<int> ans;  
int N;  
int arr[100];  
int MAX;//最大刻度  
int staus; //满足要求刻度状态  
  
void bfs()  
{  
    Node temp;temp.found=0;temp.st.insert(0);  
    queue<Node> q;  
    q.push(temp);  
    while(!q.empty())  
    {  
        Node cur = q.front();q.pop();  
        if(cur.found==staus)  
        {  
            if(cur.st.size()<ans.size())  
                ans = cur.st;  
            else if( *cur.st.rbegin() < *ans.rbegin())  
                ans = cur.st;  
            return;  
        }  
        for(set<int>::iterator iter=cur.st.begin(); iter!=cur.st.end(); iter++) //遍历可表示刻度  
        {  
            Node next;int i;  
            for(i=0; i<N; i++)  
            {  
                //如果已被选取  
                if(cur.found&(1<<i)) continue;              //刻度已选取  
                int v = *iter+arr[i];  
                if(cur.st.find(v)!=cur.st.end()) continue;//刻度已可表示  
                if(v>MAX)  continue;//超过最大刻度,不需要  
                next=cur;  
                next.st.insert(v);  
                //添加新增此刻度后可表示的新刻度  
                for(set<int>::iterator iter2=cur.st.begin();iter2!=cur.st.end(); iter2++)  
                {  
                    int x=abs(v-*iter2);  
                    if(idx[x]!=-1)  
                    {  
                        next.found|=(1<<idx[x]);  
                    }  
                }  
                if(next.found!=cur.found)  
                    q.push(next);  
            }  
            for(i=0; i<N; i++)  
            {  
                //如果已被选取  
                if(cur.found&(1<<i)) continue;              //刻度已选取  
                int v = *iter-arr[i];  
                if(cur.st.find(v)!=cur.st.end()) continue;//刻度已可表示  
                if(v>MAX)  continue;//超过最大刻度,不需要  
                if(v<0)     continue;  
                next=cur;  
                next.st.insert(v);  
                //添加新增此刻度后可表示的新刻度  
                for(set<int>::iterator iter2=cur.st.begin();iter2!=cur.st.end(); iter2++)  
                {  
                    int x=abs(v-*iter2);  
                    if(idx[x]!=-1)  
                    {  
                        next.found|=(1<<idx[x]);  
                    }  
                }  
                if(next.found!=cur.found)  
                    q.push(next);  
            }  
        }  
    }  
}  
int main()  
{  
    int i,cnt=1;  
#ifdef LOCAL  
    freopen("c:\\1.txt","r",stdin);  
#endif  
    while(scanf("%d",&N)==1)  
    {  
        if(N==0) break;  
        memset(idx,-1,sizeof(idx));  
        ans.clear();  
        for(i=0; i<N; i++)  
        {  
            scanf("%d",&arr[i]);  
            ans.insert(arr[i]);  
        }  
        sort(arr,arr+N);  
        N = unique(arr,arr+N) - arr;  
        MAX=arr[N-1];  
        for(int i=0; i<N; i++)  
            idx[arr[i]] = i;  
        staus = (1<<N)-1;  
        bfs();  
        printf("Case %d:\n%d\n",cnt++,ans.size());  
        for(set<int>::iterator iter=ans.begin(); iter!=ans.end(); ++iter)  
            printf("%d ",*iter);  
        printf("\n");  
    }  
    return 0;  
}  



  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值