鹰蛋(北大2023年最强新生的题)

12d6e32afbb645d28b30bce7afc160bf.png

这个牛马,在我准备上床玩手机之前发这一道题(23:00),然后我想到23:43,没想出来,就和他说明天再想,躺床上玩手机到00:30,过程中脑子里萦绕着这道题,然后准备睡觉,睡不着,一直想着怎么解题,然后准备起来写思路了,一看手机,1:00,最后写到了1:41,题解出来了,安然睡觉,第二天早八,想死

那么如何动态规划?

设行代表蛋数,列代表层数

当求dp[i][j]的时候,设第一颗蛋放在第k层,如果蛋碎了,此时的试验数是1+dp[i - 1][j - k],如果蛋没碎,此时的试验数是1+dp[i][k - 1],因为是最坏结果,所以第一颗蛋放在第k层的最坏结果Sk = max{1+dp[i - 1][j - k],1+dp[i][k - 1]}

而dp[i][j] = min{Sk}(k = 1,2,3……j}

这样就求完了

但是还是得优化

我画了一个图像:

6b5f55d916ff418bbde1e78eed7ad908.jpeg

下面的N要改成j

要求的dp[i][j]就在交点附近,而Sk是组合起来的,位于上方的线

就可以用二分法来求

代码如下:

#include<stdio.h>
#include<stdlib.h>
void dichotomy(int A, int B, int j);//j代表此时的最高层
int N, M, minn, * dp_pvs, * dp_nxt;//previous, next

int main(void)
{
    while(1)
    {
        //输入
        scanf("%d%d", &N, &M);
        if(N == 0)  break;
        //预处理
        dp_pvs = (int *)malloc(N * sizeof(int)), dp_nxt = (int *)malloc(N * sizeof(int));
        for(int i = 1; i <= N; i++)
            * (dp_pvs + i) = i;
        //开始动态规划
        for(int i = 2; i <= M; i++)
        {
            for(int j = 1; j <= i; j++)
                * (dp_nxt + j) = * (dp_pvs + j);
            for(int j = i + 1; j <= N; j++)
            {
                //二分法求最优值
                int tmp1 = * (dp_nxt + (j - i)) + 1, tmp2 = * (dp_pvs + j - 1) + 1;
                minn = (tmp1 < tmp2) ? tmp1 : tmp2;
                if(j - i > 1)  dichotomy(i, j, j);
                * (dp_nxt + j) = minn;
            }
            //一层求完,交换指针
            int * tmp = dp_pvs;
            dp_pvs = dp_nxt;
            dp_nxt = tmp;
        }
        //输出
        printf("%d\n", * (dp_pvs + N));
        free(dp_nxt), free(dp_pvs);
    }

    return 0;
}
void dichotomy(int A, int B, int j)
{
    int k = (A + B) / 2;
    int tmp1 = * (dp_pvs + k - 1) + 1, tmp2 = * (dp_nxt + (j - k)) + 1;//碎了和没碎
    int maxn = (tmp1 > tmp2) ? tmp1 : tmp2;
    minn = (minn < maxn) ? minn : maxn;

    if(tmp1 > tmp2 && k - A > 1)  dichotomy(A, k, j);
    else if(tmp1 < tmp2 && B - k > 1)  dichotomy(k, B, j);
    return;
}

超时了

改了一下,用了链表,对于多组数据可以一次算完

代码如下:

#include<stdio.h>
#include<stdlib.h>
typedef int ElemType;
typedef struct LNode{
	ElemType n;
    ElemType m;
    ElemType id;
	struct LNode * next;
}LinkList;
LinkList * creat(int n);
void DestroyList(LinkList * head);
void dichotomy(int A, int B, int j);//j代表此时的最高层
int N, M, minn, count = 1, * result, * dp_pvs, * dp_nxt;//previous, next

int main(void)
{
    //输入
    LinkList * head = creat(1);
    head->next->id = 0;
    while(1)
    {
        int n, m;
        scanf("%d%d", &n, &m);
        if(!n) break;
        N = (N > n) ? N : n, M = (M > m) ? M : m;
        //插入排序
        for(LinkList * node = head->next, * f_node = head; 1; node = node->next, f_node = f_node->next)
        {
            if(node == NULL || m < node->m || (m == node->m && n < node->n))
            {
                LinkList * tmp = (LinkList *)malloc(sizeof(LinkList));
                f_node->next = tmp, tmp->next = node;
                tmp->m = m, tmp->n = n, tmp->id = count;
                break;
            }
        }
        count++;
    }
    result = (int *)malloc(count * sizeof(int));;
    //预处理
    dp_pvs = (int *)malloc(N * sizeof(int)), dp_nxt = (int *)malloc(N * sizeof(int));
    LinkList * node = head->next;
    for(int i = 1; i <= N; i++)
    {
        * (dp_pvs + i) = i;
        if(node->m == 1 && node->n == i)
        {
            * (result + node->id) = i;
            node = node->next;
        }
    }
    //开始动态规划
    for(int i = 2; i <= M; i++)
    {
        for(int j = 1; j <= i; j++)
            * (dp_nxt + j) = * (dp_pvs + j);
        for(int j = i + 1; j <= N; j++)
        {
            //二分法求最优值
            int tmp1 = * (dp_nxt + (j - i)) + 1, tmp2 = * (dp_pvs + j - 1) + 1;
            minn = (tmp1 < tmp2) ? tmp1 : tmp2;
            dichotomy(i, j, j);
            * (dp_nxt + j) = minn;
            //判断并放入result
            if(node->m == i && node->n == j)
            {
                * (result + (node->id)) = minn;
                node = node->next;
                if(node == NULL)  break;
            }
        }
        //一层求完,交换指针
        int * tmp = dp_pvs;
        dp_pvs = dp_nxt;
        dp_nxt = tmp;
    }
    //输出
    for(int i = 0; i < count; i++)
        printf("%d\n", * (result + i));
    DestroyList(head);

    return 0;
}
LinkList * creat(int n)
{
	LinkList * head, * node, * end;//定义头节点,普通节点,尾部节点
	head = (LinkList *)malloc(sizeof(LinkList));//分配地址
	end = head;
	for(int i = 0; i < n; i++)
	{
		node = (LinkList *)malloc(sizeof(LinkList));//分配地址
		scanf("%d%d", &node->n, &node->m);
        N = (N > node->n) ? N : node->n, M = (M > node->m) ? M : node->m;
		end->next = node;
		end = node;
	}
	end->next = NULL;//结束创建
	return head;
}//创建长度为n的链表
void DestroyList(LinkList * head)
{
	LinkList * end = head;
	do
	{
		end = end->next;
		free(head);
		head = end;
	}while(end != NULL);
	return;
}//销毁链表
void dichotomy(int A, int B, int j)
{
    if(B - A <= 1) return;
    int k = (A + B) / 2;
    int tmp1 = * (dp_pvs + k - 1) + 1, tmp2 = * (dp_nxt + (j - k)) + 1;//碎了和没碎
    int maxn = (tmp1 > tmp2) ? tmp1 : tmp2;
    minn = (minn < maxn) ? minn : maxn;

    if(tmp1 > tmp2)  dichotomy(A, k, j);
    else if(tmp1 < tmp2)  dichotomy(k, B, j);
    return;
}

如果还是超时就是我太菜了

多学学算法吧

(补充:这题好像挺典的,网上有题解,我用的算是暴力枚举,虽然用了二分法优化使枚举不超过log2(n)次,但是效率不够)

补充:下面的代码优化了链表的使用,再加了一个新的算法:

if(m >= (int)round(log(1.0 * (n + 1)) / log(2.0)))
    result = (int)round(log(1.0 * (n + 1)) / log(2.0));

代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<math.h>
typedef int ElemType;
typedef struct LNode{
	ElemType n;
    ElemType m;
    ElemType id;
	struct LNode * next;
}LinkList;
LinkList * creat(int n);
void DestroyList(LinkList * head);
void dichotomy(int A, int B, int j);//j代表此时的最高层
int N, M, minn, count, * result, * dp_pvs, * dp_nxt;//previous, next

int main(void)
{
    //输入
    LinkList * head = (LinkList *)malloc(sizeof(LinkList));
    head->next = NULL;
    while(1)
    {
        int n, m;
        scanf("%d%d", &n, &m);
        if(!n) break;
        if(m < (int)round(log(1.0 * (n + 1)) / log(2.0)))
            N = (N > n) ? N : n, M = (M > m) ? M : m;
        //插入排序
        for(LinkList * node = head->next, * f_node = head; 1; node = node->next, f_node = f_node->next)
        {
            if(node == NULL || m < node->m || (m == node->m && n < node->n))
            {
                LinkList * tmp = (LinkList *)malloc(sizeof(LinkList));
                f_node->next = tmp, tmp->next = node;
                tmp->m = m, tmp->n = n, tmp->id = count;
                break;
            }
        }
        count++;
    }
    //预处理
    dp_pvs = (int *)malloc(N * sizeof(int)), dp_nxt = (int *)malloc(N * sizeof(int));
    result = (int *)malloc(count * sizeof(int));
    LinkList * node = head->next;
    while(node != NULL && node->m >= (int)round(log(1.0 * (node->n + 1)) / log(2.0)))
    {
        * (result + node->id) = (int)round(log(1.0 * (node->n + 1)) / log(2.0));
        node = node->next;
    }
    for(int i = 1; i <= N; i++)
    {
        * (dp_pvs + i) = i;
        if(node->m == 1 && node->n == i)
        {
            * (result + node->id) = i;
            node = node->next;
            while(node != NULL && node->m >= (int)round(log(1.0 * (node->n + 1)) / log(2.0)))
            {
                * (result + node->id) = (int)round(log(1.0 * (node->n + 1)) / log(2.0));
                node = node->next;
            }
        }
    }
    //开始动态规划
    for(int i = 2; i <= M; i++)
    {
        for(int j = 1; j <= i; j++)
            * (dp_nxt + j) = * (dp_pvs + j);
        for(int j = i + 1; j <= N; j++)
        {
            //二分法求最优值
            int tmp1 = * (dp_nxt + (j - i)) + 1, tmp2 = * (dp_pvs + j - 1) + 1;
            minn = (tmp1 < tmp2) ? tmp1 : tmp2;
            dichotomy(i, j, j);
            * (dp_nxt + j) = minn;
            //判断并放入result
            if(node->m == i && node->n == j)
            {
                * (result + (node->id)) = minn;
                node = node->next;
                while(node != NULL && node->m >= (int)round(log(1.0 * (node->n + 1)) / log(2.0)))
                {
                    * (result + node->id) = (int)round(log(1.0 * (node->n + 1)) / log(2.0));
                    node = node->next;
                }
                if(node == NULL)  break;
            }
        }
        //一层求完,交换指针
        int * tmp = dp_pvs;
        dp_pvs = dp_nxt;
        dp_nxt = tmp;
    }
    //输出
    for(int i = 0; i < count; i++)
        printf("%d\n", * (result + i));
    DestroyList(head);

    return 0;
}
void DestroyList(LinkList * head)
{
	LinkList * end = head;
	do
	{
		end = end->next;
		free(head);
		head = end;
	}while(end != NULL);
	return;
}//销毁链表
void dichotomy(int A, int B, int j)
{
    if(B - A <= 1) return;
    int k = (A + B) / 2;
    int tmp1 = * (dp_pvs + k - 1) + 1, tmp2 = * (dp_nxt + (j - k)) + 1;//碎了和没碎
    int maxn = (tmp1 > tmp2) ? tmp1 : tmp2;
    minn = (minn < maxn) ? minn : maxn;

    if(tmp1 > tmp2)  dichotomy(A, k, j);
    else if(tmp1 < tmp2)  dichotomy(k, B, j);
    return;
}

关于这一题居然有相关的论文(《从《鹰蛋》一题浅析对动态规划算法的优化》),这里是添加了论文里面的算法二,我原来的用的是算法三

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值