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;
}