小试牛刀(一)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hqh131360239/article/details/82701622

1、有10张饼,每天至少吃一张,问第三天吃完的概率(36/512)

思路:这里的三天吃完,是指恰巧第三天吃完;那么第一天最多吃8张,剩下两天对应的只有1种情况,第一天吃7张,剩下两天对应的有2种情况,第一天吃6张,剩下两天对应的有3种情况,同理依次类推:1+2+3+4+5+6+7+8=36。

10张饼一共有多少种吃发?可以第1天吃完,可以两天吃完,可以三天吃完......最多十天吃完。

方法一:可以暴力一下,第一天吃完的情况,第二天吃完的情况,第三天吃完的情况,第四天吃完的情况......第十天吃完的情况。代码如下所示。

#include <iostream>
using namespace std;
int main(){
    int sum=0;
    for(int i=1;i<=10;i++)
    for(int j=1;j<=10-i;j++)
    for(int k=1;k<=10-i-j;k++)
        if(i+j+k==10)sum++;
    //36
    int ans=0;
    for(int i=1;i<=10;i++){
        if(i==10) {ans++;break;}
        for(int j=1;j<=10-i;j++){
            if(i+j==10) {ans++;break;}
            for(int k=1;k<=10-i-j;k++){
                if(i+j+k==10) {ans++;break;}
                for(int l=1;l<=10-i-j-k;l++){
                    if(i+j+k+l==10) {ans++;break;}
                    for(int m=1;m<=10-i-j-k-l;m++){
                        if(i+j+k+l+m==10) {ans++;break;}
                        for(int n=1;n<=10-i-j-k-l-m;n++){
                            if(i+j+k+l+m+n==10) {ans++;break;}
                            for(int o=1;o<=10-i-j-k-l-m-n;o++){
                                if(i+j+k+l+m+n+o==10) {ans++;break;}
                                for(int p=1;p<=10-i-j-k-l-m-n-o;p++){
                                    if(i+j+k+l+m+n+o+p==10) {ans++;break;}
                                    for(int q=1;q<=10-i-j-k-l-m-n-o-p;q++){
                                         if(i+j+k+l+m+n+o+p+q==10) {ans++;break;}
                                         for(int r=1;r<=10-i-j-k-l-m-n-o-p-q;r++){
                                            if(i+j+k+l+m+n+o+p+q+r==10) {ans++;break;}
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    //512
    cout<<sum<<endl;
    cout<<ans<<endl;
}

方法二:动态归回,在方法一种不难发现,比如第n天恰好吃完m张饼的情况=第n-1天吃小于m-1张饼的情况和。

dp[i,j]=dp[i-1][i...j-1],如dp[3,10]表示第3天恰巧吃10张饼的情况,那么等于第2天吃小于10张饼的所有情况之和。

第2天最少吃2张饼,1种情况,那么第3天对应吃8张,

第2天吃3张饼的情况是2种,那么对应第3天吃饼是7张,

第2天吃4张饼的情况是3种,那么对应第3天吃饼是6张,

第2天吃5张饼的情况是4种,那么对应第3天吃饼是5张,

第2天吃6张饼的情况是5种,那么对应第3天吃饼是4张。

第2天吃7张饼的情况是6种,那么对应第3天吃饼是3张。

第2天吃8张饼的情况是7种,那么对应第3天吃饼是2张。

第2天吃9张饼的情况是8种,那么对应第3天吃饼是1张。dp[3,10]=36

吃完十张饼的情况:dp[1,10]+dp[2,10]+dp[3,10]+dp[4,10]+dp[5,10]+dp[6,10]+dp[7,10]+dp[8,10]+dp[9,10]+dp[10,10]=512(表示第一天吃完10张饼的情况+第i天吃完10张饼的情况+第十天吃完10张饼的情况)

动态转移方程:dp[i][j]=dp[i][j-1]+dp[i-1][j-1](dp[i][j-1]=前一天的前j-2张饼的所有情况)

2、输入两个单调递增的链表,输出两个链表合成后的链表,合成后的链表满足单调不减原则。

思路:三个while循环即可,后两个while循环用一个三木运算符替代了。时间复杂度应该是O(n+m),有头结点,pa和pb分别指向第一个节点,pc指向的是头结点。

void MergeList(LinkList &A,LinkList &B,LinkList &C)
{
    pa=A->next;
    pb=B->next;
    pc=C=A;    //用A的头结点作为C的头结点
    while(pa&&pb){
        if(pa->data<=pb->data){
            pc->next=pa;pc=pc->next;
            pa=pa->next;
        }else{
            pc->next=pb;pc=pb;
            pb=pb->next;
        }
    }
    pc->next=pa?pa:pb;
    free(B);
}

3、输入一颗二叉树的节点和一个整数,打印出二叉树中节点的值和为输入整数的所有路径。路径定义为从树的根节点开始往下直到叶子节点所经过节点形成的一条路径。

如上图二叉树和输入一个整数22,那么结果:

1->2->7->12、1->3->5->13、1->3->4->14

思路:其实以前遇到过,但是是求情况,没有要求打印每种情况的路径。注意在递归的时候递归到叶子节点之后,还会递归到叶子节点的左右子树,不过只是空操作,但是这个流程是存在的。这里的剪枝没有最优化,其实还可以根据sum的值来进行剪枝。

#include<iostream>
using namespace std;
int a[16]={0,1,2,3,7,6,5,4,8,12,9,10,11,13,14,15};   //15个结点
int x=22;
int count=0;
void dfs(int o,int n,int sum){
    if(o>n) return;
    sum+=a[o];
    if(sum==x){
        count++;
    }
    dfs(2*o,n,sum);
    dfs(2*o+1,n,sum);
}
int main(){
    dfs(1,15,0);
    cout<<count<<endl;
}
#include<iostream>
#include<vector>
using namespace std;
vector<int> ans;
int a[16]={0,1,2,3,7,6,5,4,8,12,9,10,11,13,14,15};
//15个结点
int x=22;
int count=0;
void dfs(int o,int n,int sum){
    if(o>n) return;
    ans.push_back(a[o]);
    sum+=a[o];
    if(sum==x){     //打印路径
        count++;
        cout<<ans[0];
        for(int i=1;i<ans.size();i++){
            cout<<"->"<<ans[i];
        }
        cout<<endl;
    }
    dfs(2*o,n,sum);
    dfs(2*o+1,n,sum);
    //该节点的左右子树都递归完毕了,才能移除
    ans.erase(ans.begin()+ans.size()-1);
}
int main(){
    ans.clear();
    dfs(1,15,0);
    cout<<count<<endl;
}

猜你喜欢

转载自blog.csdn.net/hqh131360239/article/details/82701622