LETTers比赛第一场 解题报告

解题报告

提交人:蔡驰宇

提交日期:2012.04.11

第1题解题报告

题目描述

题目提供的Fibonacci的递推公式,以及相应的初值,要求第n个Fibonacci数。

题面建模

由Fibonacci递推公式和测试数据可知int型与_64int型数据无法满足题目求解要求,因而要使用高精度算法,即为大数问题,利用数组来进行计算。

解题要点

字符串与数组之间的转换,大数数组的基本运算方法。

时空开销分析

特别说明

程序

#include <iostream>

#include <string>

using namespace std;

int n;

int f[5][2100];

void init()

{

    memset(f, 0, sizeof(f));

    f[0][0] = 1;

    f[1][0] = 1;

    f[2][0] = 1;

    f[3][0] = 1;

    n--;

}

int fib()

{

    if(n < 4) return 1;

    int i, j, r, t, m;

    for(i=4, m=1; i<=n; i++)

    {

        for(j=0, r=0; j<m; j++)

        {

            t = f[(i-1)%5][j]+f[(i-2)%5][j]+f[(i-3)%5][j]+f[(i-4)%5][j] + r;

            f[i%5][j] = t % 10;

            r = t / 10;

        }

        if(r > 0)

        {

            f[i%5][j] = r;

            m++;

        }

    }

    return m;

}

void display(int m)

{

    int j;

    for(j=m-1; j>=0; j--)

    {

        printf("%d", f[n%5][j]);

    }

    printf("\n");

}

int main()

{

    while(scanf("%d", &n) != EOF)

    {

        init();

        int m = fib();

        display(m);

    }

    return 0;

}

第2题解题报告

提交人:蔡驰宇

提交日期:2012.04.11

题目描述

题目给定三个字符串,判断前两个字符串是否能够按题所述的规则组合成第三个字符串。

题面建模

题意简化后可以很明显的看出是一个DFS问题,在使用DFS的同时还要结合记忆化搜索的方法。

解题要点

时空开销分析

特别说明

本题也可以使用BFS算法。

程序

#include <iostream>

using namespace std;

bool flag, hash[205][205];

int len, len1, len2;

char s[405], s1[205], s2[205];  

void dfs (int i, int j, int k)  //字符串匹配型深搜,s1[i]和s2[j]分别跟s[k]匹配

{

    if (flag)

        return ;

    if (k == len)    //匹配成功

    {

        flag = true;

              return ;

    }

   if (hash[i][j]) //hash法记忆状态,若状态再次出现则已经不可能匹配成功,记忆化搜索

        return ;

    hash[i][j] = true;

    if (!flag && s1[i] == s[k])

        dfs (i+1, j, k+1);

    if (!flag && s2[j] == s[k])

        dfs (i, j+1, k+1);

}

int main()

{

    int t, x = 1;

    scanf ("%d", &t);

    while (t--)

    {

        scanf ("%s%s%s", s1, s2, s);

        len1 = strlen(s1);

        len2 = strlen(s2);

        len = strlen(s);

        memset (hash, false, sizeof(hash));

        flag = false;

        dfs (0, 0, 0);

        printf ("Data set %d: ", x++);

        if (flag)

            printf ("yes\n");

        else printf ("no\n");

    }

    return 0;

}

第3题解题报告

提交人:蔡驰宇

提交日期:2012.04.11

题目描述

给一个字母锁,含有n个字母,然后给定m个区间,并规定区间里面的那一段字母是可以同时改变的,比如a变为b,b变为c,z变为a之类的,然后如果锁可以通过有限次变换变成相同的,就规定为同一把锁。然后要求有多少把不同的索。

题面建模

首先确认下如果没有这种区间存在,那么锁的种类总共有26^n个,而出现了一个区间n就得减去1,因为无论是在哪个区间,出现了一个区间,就代表这个区间有26种相同的排列组合要排除掉。结果就由原来的26^n变为了现在的26^(n-1)。

解题要点

通过并查集的方法来生成最小树,从而可以得出结果。

时空开销分析

特别说明

在设计程序时需要注意一种特殊情况,就是比如有一个区间1~5,还有一个区间1~2,那么就等于隐含了一个存在的区间3~5,因为区间3~5可以由前两个区间生成。

程序

#include<iostream>

using namespace std;

const int MAX = 10000005;

const int MOD = 1000000007;

typedef struct set

{

    int parent;

} S;

S set[MAX];

int findParent(int x)

{

    if(x != set[x].parent)

        set[x].parent = findParent(set[x].parent);

    return set[x].parent;

}

int Union(int x, int y)

{

    x = findParent(x);

    y = findParent(y);

    if(x == y)

        return 0;

    //if(set[x].rank < set[y].rank)

    {

        set[x].parent = y;

    }

    /*else

    {

           set[y].parent = x;

           if(set[y].rank == set[x].rank)

                  set[x].rank++;

    }*/

    return 1;

}

void makeSet()

{

    for(int i = 0; i < MAX; i++)

    {

        set[i].parent = i;

        //set[i].rank = 0;

    }

}

__int64 Exp(__int64 a, __int64 x)//二分求幂

{

    __int64 b;

    if(x == 0)

        return 1;

    b = Exp(a, x / 2);

    b = b * b % MOD;

    if(x & 1)

        b = b * 26 % MOD;

    return b;

}

int main(void)

{

    int n, m;

    while(scanf("%d %d", &n, &m) == 2)

    {

        makeSet();

        int ans = 0;

        for(int i = 0; i < m; i++)

        {

            int s, e;

            scanf("%d %d", &s, &e);

            ans += Union(s, e + 1);

        }

        printf("%I64d\n", Exp((__int64)26, (__int64)(n - ans)));

    }

    return 0;

}

第4题解题报告

提交人:蔡驰宇

提交日期:2012.04.11

题目描述

有n个人要去聚会,然后这些人都是一个公司的,有上下司关系,要求邀请最多的人,并且这些人之间没有从属关系。并且在最后要判断结果是否唯一。

题面建模

建一个n节点的关系数,每个节点代表一个人。从中选择一些点,使这些点均不存在亲子关系,最多能取多少个点,并且判断取法是否唯一。

解题要点

树状DP+一个判断。设dp[i][0]为在以i为根的子树中,不选择点i最多能够选的数目,dp[i][1]为选择i点的最多数目。

状态转移方程:

当i为叶子节点时:

dp[i][0]=0;

dp[i][1]=1;

当i为非叶子节点时:

dp[i][0]=sum(max(dp[j][0],dp[j][1])) (j为i的儿子)

dp[i][1]=sum(dp[j][0]) (j为i的儿子)

时空开销分析

特别说明

解的唯一性的判断:

设u[i][x]为0时表示dp[i][x]的解唯一,为1则表示不唯一.

当x为0时,若存在j是i的儿子,使得dp[j][0]>dp[j][1]且u[j][0]=1,或dp[j][0]<dp[j][1]且u[j][1]=1或dp[j][0]=dp[j][1],则u[i][0]=1;

当x为1时,若存在j是i的儿子,使得u[j][0]=1,则u[i][0]=1;

程序

#include<stdio.h>

#include<string.h>

#define MAXN 205

struct Node

{

    char name[105];

    Node *next;

}node[MAXN];

struct List

{

    int node;

    List *next;

}head[MAXN];

int dp[MAXN][2],u[MAXN][2];

void DP(int node)

{

    List *p;

    int sumdp=0;

    if (head[node].next==NULL)

    {

        dp[node][0]=0;

        dp[node][1]=1;

        u[node][0]=0;

        u[node][1]=0;

    }

    else

    {

        p=head[node].next;

        while (p!=NULL)

        {

           DP(p->node);

            if (dp[p->node][0]>dp[p->node][1])

            {

                sumdp+=dp[p->node][0];

                if (u[p->node][0]==1) u[node][0]=1;

                     }

            else

            if (dp[p->node][0]<dp[p->node][1])

            {

               sumdp+=dp[p->node][1];

                if (u[p->node][1]==1) u[node][0]=1;

            }

            else

            {

                sumdp+=dp[p->node][0];

                u[node][0]=1;

            }

            dp[node][1]+=dp[p->node][0];

            if (u[p->node][0]==1) u[node][1]=1;

            p=p->next;

              }

       dp[node][0]=sumdp;

        dp[node][1]+=1;

    }

}

int main()

{

    List *s;

    int i,j,n;

    char s1[MAXN][105],s2[MAXN][105];

    while (scanf("%d",&n)&&n)

    {

        memset(dp,0,sizeof(dp));

        memset(u,0,sizeof(u));

        memset(s1,0,sizeof(s1));

        memset(s2,0,sizeof(s2));

        for (i=0;i<=n;++i) head[i].next=NULL;

        getchar();

        scanf("%s",node[0].name);

        getchar();

        for (i=1;i<n;++i)

        {

            scanf("%s %s",s1[i],s2[i]);

            getchar();

            strcpy(node[i].name,s1[i]);

        }

        for (i=1;i<n;++i)

        {

            for (j=0;j<n;++j)

            {

                if (strcmp(s2[i],node[j].name)==0)

                {

                    s=new List;

                    s->node=i;

                    s->next=head[j].next;

                    head[j].next=s;

                    break;

                }

            }

        }

        DP(0);

        if (dp[0][0]>dp[0][1])

        {

            printf("%d ",dp[0][0]);

            if (u[0][0]) printf("No\n");

            else printf("Yes\n");

        }

        else

        if (dp[0][0]<dp[0][1])

        {

            printf("%d ",dp[0][1]);

            if (u[0][1]) printf("No\n");

            else printf("Yes\n");

        }

        else

        printf("%d No\n",dp[0][0]);

    }

    return 0;

}

原文地址:https://www.cnblogs.com/LETTers/p/2442975.html