soj4059 Towns along a Highway解题报告 经典dfs

这是11年集训时放的题目,当时深为不解,题目来源是coci。

【大意】

设有n个城镇city[0],city[1],……,city[n-1],city[i]与city[i+1]相邻( i 属于[0,n-2] ),其距离记为dist[i],若给出每两个城市之间的距离,则可以算出任意两个城市间的距离,共有n*(n-1)/2个。

现在给出这n*(n-1)/2个距离,求每两个城市之间的距离,答案可能不存在或者有很多个。

若有多个,则按照dist[i]的字典序输出。n<=20

【分析】

由于n很小,可以考虑搜索算法。

如果枚举相邻2个城市间的距离,则其复杂度为n^(n*(n-1)/2),最大为20^190,完全无法容忍。

其实这道题和论文里的一篇及其相似,关于搜索对象的问题,不同的搜索对象,不同的搜索顺序都极大的影响搜索效率。

这里可以搜索相邻2个城市间的距离,或者第1个城市到第i个城市之间的距离。

先上算法吧:

记sum[i]=dist[0] + ... + dist[i]。

分析知:最大值必为第一个城市到第n个城市间的距离,记为maxLength。

那么第二大的值呢??

显然它为dist[0]+...+dist[n-2]或者dist[1]+...+dist[n-1],否则反证法,第二大的值必然不是dist[0]+...+dist[n-1],故必是dist[0]+...+dist[n-2]或者dist[1]+...+dist[n-1]的一部分(又假设知不是全部),记为dist[i]+..+dist[j],其值显然小于前面两个,但是由于它是第二大的,那么前两个值必然不存在于那任意两个城市间的距离,矛盾,故。。

那么第三大的呢??

类似。。

假设当前最大的没用到的值为k,其中前i个和后j个距离和都算出来了,即已经搜到了sum[0]到sum[i-1]和sum[n+1-j]到sum[n],那么k必然是sum[n-j]或者maxLength-k是sum[i]。

证法同上。

于是其复杂度为O(2^n),搜索深度为n,每个值k有2次向下扩展的机会,故为O(2^n)。

这里注意每次放置的时候将改点到其他已知点的距离都减掉。

附个链接:soj4059

附个代码:


#include <string.h>
#include <stdio.h>
#include <set>
#include <ctype.h>
#include <algorithm>
#include <queue>
#include <string.h>

using namespace std;

const int maxn = 512 ;
int ans[32] , dist[maxn] , cnt[maxn] , end , num[maxn] , myCount[maxn] , top , n , iCount ;
vector<int> ve ;

struct node {
    int array[20] ;
}hb[maxn*maxn] ;

struct cmp {
    int operator()(const int a,const int b) const
    {
        return memcmp(hb[a].array,hb[b].array,sizeof(int)*(n-1)) < 0 ;        
    }
};


inline bool get(int &t)
{
    bool flag = 0 ;
    char c;
    while(!isdigit(c = getchar())&&c!='-') if( c == -1 ) break ;
    if( c == -1 ) return 0 ;
    if(c=='-') flag = 1 , t = 0 ;
    else t = c ^ 48;
    while(isdigit(c = getchar()))    t = (t << 1) + (t << 3) + (c ^ 48) ;
    if(flag) t = -t ;
    return 1 ;
}

inline int fabs(int x) {    return x > 0 ? x : -x ;    }

void dfs(int);

set<int,cmp> st ;

void nimei(int pos,int value)
{
    int j , k ;
    bool sth = true ;
    for( j = n ; j > pos ; j--) 
    {
        myCount[cnt[k=fabs(ans[j]-value)]]--;
        if(myCount[cnt[k]]<0) sth = false ;
    }
    if(sth)
    {
        ans[pos] = value ;
        dfs(pos-1);
    }
    for( j = n ; j > pos ; j--) myCount[cnt[fabs(ans[j]-value)]]++;    
}

/*
5
9 8 7 6 6 4 3 2 2 1
*/

void dfs(int pos)
{
    int i ;
    if( pos == 0 )    
    {
        int j = n-1 ;
        memcpy(hb[iCount].array,ans+1,sizeof(int)*j);
        sort(hb[iCount].array,hb[iCount].array+j);
        if( st.count(iCount) == 0 ) st.insert(iCount++);
        return ;
    }
    for ( i = top-1 ; i >= 1 ; i--) if(myCount[i]) break ;
    if( i < 1 ) return ;
    nimei(pos,dist[0]-num[i]);
    nimei(pos,num[i]);
}

int main()
{
    int i , j ;
    myCount[0] = 0 ;
    while (get(n)&&n>0)
    {
        iCount = 0 ;
        st.clear();
        memset(cnt,0,sizeof(cnt));
        int maxNum = 0 ;
        for(i = 0 ; i < n*(n-1)/2 ; i++) 
        {
            get(dist[i]);
            cnt[dist[i]]++;
        }
        for( i = 1 , top = 1 ; i <= dist[0] ; i++) if(cnt[i])
        {
            num[top] = i ;
            myCount[top] = cnt[i] ;
            cnt[i] = top++;
        }    
        ans[n] = 0 ;
        ans[n-1] = dist[0] ;
        myCount[cnt[dist[0]]]--;
        dfs(n-2);
        set<int,cmp>::iterator ite = st.begin() ;
        for( ; ite != st.end() ; ite++)
        {
            i = *ite ;
            printf("%d",hb[i].array[0]);
            for( j = 1 ; j < n-1 ; j++) printf(" %d",hb[i].array[j]-hb[i].array[j-1]);
            puts("");
        }
        puts("-----");
    }
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值