zoukankan      html  css  js  c++  java
  • D

    D - Series-Parallel Networks

    UVA - 10253

    题意就不描述了

    输入格式:

    ​ 输入包含多组式布局,每组数据仅包含一个整数,即边数n(1<=n<=30)。输入结束标志为n=0。

    输出格式:

    ​ 对于每组数据,输出一行,即包含m条边的串并联网络的数目。

    这是白皮书上117页的内容,其中给出了两个算法,第一个算法理解但是有点难实现,第二个算法是真心不懂(但是第二个算法代码确实很简单dp吧)

    第一个算法:

    ​ 把串并联网络看作一个树,因为每个串联网络一个分成多个并联网络或者单边 串联,并联网络可以分成

    多个串联网络或者单边 并联。可以想想成一棵树的形式,假设刚开始有一个根是一个串连网络(并联网络),下一层全是并联网络(串联网络),就这样交替出现,当然每一棵树的最少得有两个儿子,如果这个棵数是个单边,那么他就是个叶子节点,没有儿子,这样问题就转化为了“共n个叶子,且每个非叶子节点至少有两个子节点”的树的数目f(n),再乘以2就是本题的答案。

    即找出n个叶子的树有多少种。

    因为题目中说串联在一起(并联在一起)的各个部分可以相互调换顺序,相当于每一层的树交换顺序后 属于同一种,所以我们只要保证从左到右节点数递增或者递减的就可以了

    假设对于5个叶子的节点,子树可以分为

    1 1 1 1 1

    1 1 1 2

    1 1 3

    1 4

    1 2 2

    2 3

    把每种的情况全加起来即可

    对于24个叶子的树:

    2 10 12 的情况共有 f(2)f(10)f(12)f(2)*f(10)*f(12)种方法

    对于 6 6 6有多少种方法吗?难道是f(6)f(6)f(6)f(6)*f(6)*f(6) 吗? 如果是这样那么你就错了,因为可能会有会有重复的,换句话说 6 6 6这三棵树还是可以互换的(而且还是保证序列递增),那么为了保证不重复

    这个问题可化为f(6)中选取3个作为一个组合,且可重复的方案数目。即可重复选取的组合问题

    应该是C(f(6)+3-1,3)种方案

    这样这道题就有苗头了:

    1.对于求f(n)枚举n的所有整数划分,(因为n并不大,所以这样方法是可行的)

    2.对于每个不同的划分求出此划分的方案数目,相加就是f(n)

    这是个递归的求法

    实现代码:

    #include<cstdio>
    #include<algorithm>
    #include<stack>
    #include<queue>
    #include<map>
    #include<vector>
    #include<cstring>
    #include<string>
    #include<iostream>
    #include<iomanip>
    #define mset(a,b)   memset(a,b,sizeof(a))
    using namespace std;
    typedef unsigned long long ull;
    typedef long long ll;
    const int maxn=30;
    const int branch=26;
    const int inf=0x3f3f3f3f;
    const int MOD=1e6+7;
    int book[maxn+5];//记忆化,book[k]=1表示f(k)已经求出来了,防止递归计算子问题
    ll vis[maxn+5];//存f(k)
    ll C(ll a,ll b)//求出组合数目
    {
        ll ans=1;
        if(b>a-b)  b=a-b;
        for(ll i=a;i>=a-b+1;--i)//乘以b个
        {
            ans*=i;
        }
        for(ll i=2;i<=b;++i)
            ans/=i;
        return ans;
    }
    void init()//初始化
    {
        mset(book,0);
        book[0]=book[1]=book[2]=1;
        vis[0]=vis[1]=vis[2]=1;
    }
    ll calc(int n);
    ll solve(vector<int>& mmp)//计算这个序列的值
    {
        ll ans=1;
        int total=1;
        for(int i=0;i<mmp.size();++i)
        {
            if(i+1<mmp.size()&&mmp[i+1]==mmp[i])
            {
                total++;
                continue;
            }
            ans*=C(calc(mmp[i])+total-1,(ll)total);
            total=1;
        }
        return ans;
    }
    void dfs(int k,int sum,ll& ans,vector<int>& mmp)//上一个数字最大为k, 还剩sum个未加. ans记录值方案为多少,用于在 sum=0的时候计算
    {
        if(sum==0)//开始计算 里面的数字都是从大到小
        {
            ans+=solve(mmp);
        }
        else//继续枚举划分
        {
            for(int i=min(k,sum);i>=1;--i)
            {
                mmp.push_back(i);
                dfs(i,sum-i,ans,mmp);
                mmp.pop_back();
            }
        }
    }
    ll calc(int n)//求f(n);
    {
        if(book[n])
            return vis[n];
        vector<int> mmp;
        ll ans=0;
        for(int i=n-1;i>=1;--i)
        {
            mmp.push_back(i);
            dfs(i,n-i,ans,mmp);
            mmp.pop_back();
        }
        book[n]=1;//记忆化计算 防止重复计算子问题
        vis[n]=ans;
        return ans;
    }
    int main()
    {
        init();
        int n;
        ios::sync_with_stdio(false);
        while(cin>>n,n)
        {
            if(n==1)
                cout<<1<<endl;
            else
                cout<<calc(n)*2<<endl;
        }
    }
    
    

    就这样吧,看代码觉得还是第二种简单,但是真心理解不动。

  • 相关阅读:
    窗口和消息
    输出文字
    《windows程序设计》第一章,建议想学API的每天看一章
    hdu 1008为何不对?求大神指导!
    课程设计(物体类),图片可能没有加载出来,自己运行一下就行了
    二叉树前序、中序、后序遍历相互求法
    哈希表工作原理
    c++中关于static关键字的问题
    Halcon学习笔记之缺陷检测(二)
    Halcon学习笔记之缺陷检测(一)
  • 原文地址:https://www.cnblogs.com/dchnzlh/p/10427281.html
Copyright © 2011-2022 走看看