Test #3 2014/12/13题解

来源:互联网 发布:下载炒股模拟软件 编辑:程序博客网 时间:2024/04/30 14:21

题目地址:http://acm.njupt.edu.cn/vjudge/contest/view.action?cid=161#overview

这次的题目比较多,8个小时的时间也比较长,题目也基本都是一些算法和数据结构的基础练习题。(C那个DP有点略难)

另外提醒下在contest结束之后,Vjudge仍然允许提交,只是不再计入rank了而已,欢迎继续练习。

下面进入正题。


A - Sum of Consecutive Prime Numbers

这题是让求一个数分解成连续的若干个质数可以有多少种分解方法。

很简单,预处理一下10000以内的素数表,然后暴力枚举即可。

较为简单,附代码:

//Origin:POJ 2739//Tags: Math #include <cstdio>#include <iostream>#include <cstring>#include <algorithm>#include <cstdlib>using namespace std; bool Prime(int num)//判断素数 {    int m;    if(num==2) return true;    for(m=2;m*m<=num;m++)        if(num%m==0)return false;    return true;}int p[2000],len,ans;int n,tot;int main(){int j=0;    for(int i=2;i<10000;i++)//预处理素数表         if(Prime(i))p[++j]=i;    len=j;    while(true){        ans=0;        scanf("%d",&n);        if(n==0)return 0;        for(int i=1;i<=len;i++){            tot=0;            for(j=i;j<=len&&tot<n;j++)                tot+=p[j];            if(tot==n)ans++;        }        printf("%d\n",ans);    }    return 0;}


B - Oulipo

这个还是KMP算法。

就是最原始的求next数组,然后进行匹配再计数可以成功匹配多少次。

附代码:

//Origin:POJ 3461//Tags:strings KMP#include <cstdio>#include <cstring>#include <cstdlib>#include <iostream>#include <algorithm>using namespace std;char w[10500],t[1001000];int nxt[10500],T,ans,lenw,lent;int main(){scanf("%d",&T);while (T--){scanf("%s",w);lenw=strlen(w);scanf("%s",t);lent=strlen(t);memset(nxt,0,sizeof nxt);nxt[0]=-1;int j=-1;for (int i=1;i<lenw;i++)//构造next数组 {while (j>-1 && w[j+1]!=w[i])j=nxt[j];if (w[j+1]==w[i])j++;nxt[i]=j;}ans=0;j=-1;for (int i=0;i<lent;i++)//KMP匹配 {while (j>-1 && w[j+1]!=t[i])j=nxt[j];if (w[j+1]==t[i])j++;if (j==lenw-1){ans++;j=nxt[j];}}printf("%d\n",ans);}}

C - Little Tiger vs. Deep Monkey

这个是一个稍微要动下脑子的题目。

题目大意就是答对一道题可以得a[i]分,然后求“至少得到多少分数使得至少有p的概率不会输“。

这样问题就转化成了求”得到这个分数的概率大于等于P的时候,所得分数的最小值“。

现在就只要求得到每个分数的概率即可,可以利用动态规划的思想。

因为是随机答题,机器人每次答题正确和错误的概率都是0.5

一开始没有答题,得零分的概率是1.0。然后回答了第i个问题的j分数可以由第i-1个问题时的概率状态来递推出 


f[i][j] += f[i-1][j]*0.5; (第i-1题获得了j分时候且第i题没有答对的情况)

f[i][j+a[i]] += f[i-1][j]*0.5 (第i-1题获得了j分时候且第i题答对的情况)


然后为了节约内存,可以做一个小优化。

因为回答了第i题时候的状态只和回答了第i-1题的时候有关,所以可以只用两个数组来不停的迭代计算,具体实现见代码。

总共只有40题,最多只会有40,000分,只开两个数组的话内存占用的非常小。

理论上如果不做这个优化也不会超出空间限制,没有具体测试,但加个优化也没多多少代码。


这题是去年长春赛区区域赛现场的第二可做的题目,签到题和这题做对了,再出一题就可以稳进铜牌区了~

附代码:

//Origin:ACM/ICPC 2013 Asia Regional Changchun;HDU 4815//Tags:DP#include <cstdio>#include <cmath>#include <cstdlib>#include <cstring>#include <algorithm>using namespace std;double P;int n,a[45],ans;double f[2][40500];//即为进行迭代的dp数组 bool cur;//表示当前使用的是哪个状态 int main(){int T;scanf("%d",&T);while (T--){int tot=0;scanf("%d%lf",&n,&P);for (int i=1;i<=n;i++){scanf("%d",&a[i]);tot+=a[i];}cur=0;memset(f,0,sizeof f );f[0][0]=1;for (int i=1;i<=n;i++)//f[j]表示答了i题之后 得到分数为j的概率 {cur=!cur;//用两个数组迭代计算节省内存空间,利用取反来实现状态的迭代 memset(f[cur],0,sizeof (f[cur]));for (int j=0;j<=tot;j++){if (f[!cur][j]>0){f[cur][j]+=0.5*f[!cur][j];f[cur][j+a[i]]+=0.5*f[!cur][j];}}}for (int i=1;i<=tot;i++)f[cur][i]+=f[cur][i-1];//经过这个处理f[i]表示回答了所有题目之后,分数小于等于i的概率  for (int i=0;i<=tot;i++)if (f[cur][i]>=P){ans=i;break;}printf("%d\n",ans);}return 0;}

D - Winner

这是Codeforce #2的A题

用了一个哈希表的基本思想。(哈希表是什么东西详细见百度百科,比起之前的几个算法数据结构非常的简单易懂)

题目是要求 最后得到最高分且第一个达到这个分数的人的名字。

C++因为map的存在就非常方便了,可以直接调用map来存储数据,C得写一个哈希函数求哈希值然后颠来倒去的算各种麻烦。。

map的具体使用方法详见C++ Primer或者http://www.cplusplus.com/,非常快捷好用的一个STL。

附代码

//Origin:CodeForces 2A#include <cstdio>#include <string>#include <algorithm>#include <map>#include <iostream>using namespace std;string s[1010];int p[1010];int n,maxm;map <string,int> mp,g;//mp存储的是每个人最后达到的分数,g存储的是当前的分数 int main(){maxm=-2147483646;mp.clear();g.clear();scanf("%d",&n);for (int i=1;i<=n;i++){cin>>s[i]>>p[i];mp[s[i]]+=p[i];}for (int i=1;i<=n;i++)maxm=max(maxm,mp[s[i]]);int ans=0;g.clear();for (int i=1;!ans&&i<=n;i++)//如果ans不为0了就退出循环 if (mp[s[i]]==maxm && (g[s[i]]+=p[i])>=maxm)ans=i;cout<<s[ans]<<endl;}


E - Zhuge Liang's Password

这是2013年杭州赛区的签到题。

要求A和B两个方阵最大的匹配数是多少。

所谓匹配数即为A和B经过任意的90°旋转后,数字完全相同的位置数。

我们就可以假设A矩阵不动,B矩阵一共有4种旋转方式,分别是转0°、90°、180°、270°。

然后依次判断就行了。(n即为题目中描述的方阵的边长,以下旋转均为顺时针旋转)

转0°的时候

A[i][j]对应的B的位置就是B[i][j]

转90°时候

A[i][j]对应的B的位置是B[j][n+1-i]

转180°时候

A[i][j]对应的B的位置是B[n+1-i][n+1-j]

转270°

A[i][j]对应的B的位置是B[n+1-j][i]

然后求一下最大的匹配数量即可。


附代码:

//Origin:ACM/ICPC 2013 Asia Hangzhou Regional Contest;HDU 4772#include <cstring>#include <cstdio>#include <cstdlib>#include <map>#include <climits>#include <algorithm>using namespace std;int p[350][350],q[350][350];int tot,ans,n;int main(){while (scanf("%d",&n)&&n!=0){ans=tot=0;memset(p,0,sizeof 0);memset(q,0,sizeof 0);for (int i=1;i<=n;i++)for (int j=1;j<=n;j++)scanf("%d",&p[i][j]);for (int i=1;i<=n;i++)for (int j=1;j<=n;j++)scanf("%d",&q[i][j]);for (int i=1;i<=n;i++)for (int j=1;j<=n;j++)if (p[i][j]==q[i][j])tot++;ans=max(ans,tot);tot=0;for (int i=1;i<=n;i++)for (int j=1;j<=n;j++)if (p[i][j]==q[j][n+1-i])tot++;ans=max(ans,tot);tot=0;for (int i=1;i<=n;i++)for (int j=1;j<=n;j++)if (p[i][j]==q[n+1-i][n+1-j])tot++;ans=max(ans,tot);tot=0;for (int i=1;i<=n;i++)for (int j=1;j<=n;j++)if (p[i][j]==q[n+1-j][i])tot++;ans=max(ans,tot);printf("%d\n",ans);}}


F - Ubiquitous Religions

题目描述的是有n个人(编号1-n),不方便问每个人信仰的什么宗教,但是目前已知有若干组两个人是信仰的相同的宗教,求最多有多少种宗教。

这还是一个基础的并查集题。

一开始假设每个人都信仰不同的宗教,每组两个人信仰相同的宗教即把这两个人分别所在的集合进行合并,最后求有多少个不同的集合即可。

运用一个小技巧,有多少人所在的集合编号依旧是初始编号,那么这个数量就是集合的数量。(为什么这么做是对的,可以自己思考下~)

附代码:

//Origin:POJ 2524//Tags:并查集 #include <cstdio>#include <cstring>#include <cstdlib>#include <algorithm>#include <iostream>using namespace std;int n,m,x,y;int f[50100];int gt_fa(int m){return m==f[m] ? m : gt_fa(f[m]);}int main(){int nm=0; while (scanf("%d%d",&n,&m)!=EOF && (n+m) ){for (int i=1;i<=n;i++)f[i]=i;for (int i=1;i<=m;i++){scanf("%d%d",&x,&y);f[gt_fa(x)]=gt_fa(y);}int tot=0;for (int i=1;i<=n;i++)tot+= gt_fa(i)==i ? 1 : 0;//可以通过有多少人还在自己初始所在编号的集合中,来判断有多少个不同的集合 printf("Case %d: %d\n",++nm,tot);}return 0;}


0 0
原创粉丝点击