湖师大ACM校赛总结

湖师大ACM校赛总结

      这是寒假后参加的第一场比赛,是湖南师范大学的校赛,居然是场个人赛!隔了一个寒假,比赛过程很不顺利!并且一直在卡一道题,直到最后也没有A来。不扯了,写份总结吧~~

 

         题目链接:http://acm.hunnu.edu.cn/online/?action=problem&type=list&courseid=55

A题:这就是一道很水的题,计算绩点的题目,如果真的要分类,就是C语言的题目了;

B题:这道题开始没读懂就跳过去了,隔了一小会儿发现很多人都做了,一看,也是C语言的题目;

C题:数论。目前还没有着重学过数论,队友说这题其实不难,以后做了数论在回来看看吧。

D题:搜索,具体点是深搜,这题自己还没有出,也没有着重学这类题目,暂时搁一搁吧。

E题:动态规划题,这道题算不上什么难题,但是赛场上自己一看就以为是KMP,没细看就舍弃了~~

           下面对这道题写个解题报告:

           定义状态:f[i][j]:为关键字key的前i位与数据库中的字串s的前j位匹配结果(bool 型变量

                        f[i][j]为1时表示匹配     

           根据关键字key的不同情况不难得出:

                当key[i]='?'时,f[i][j]=f[i-1][j-1];

                当key='*'时,f[i][j]=f[i-1][j]||f[i][j-1];

                当key[i]为小写字母,则f[i][j]=(f[i-1][j-1])&&(key[i]==s[j]);

               #include<iostream> using namespace std; char key[51],s[51]; bool dp(){ int m=strlen(key),n=strlen(s); bool f[51][51],flag=1; for(int i=1;i<=m;i++){ //这是本题初始化上的一个细节,可以结合程序尾附的测试数据理解 if(flag && key[i-1]=='*') f[i][0]=1; else f[i][0]=0,flag=0; } for(int i=1;i<=n;i++) f[0][i]=0; f[0][0]=1; for(int i=1;i<=m;i++) for(int j=1;j<=n;j++) if(key[i-1]=='*') f[i][j]=f[i-1][j]+f[i][j-1]; else if(key[i-1]=='?') f[i][j]=f[i-1][j-1]; else f[i][j]=(f[i-1][j-1])&(key[i-1]==s[j-1]); return f[m][n]; } int main(){ while(scanf("%s",key)!=-1){ int n,cnt=0; scanf("%d",&n); for(int i=0;i<n;i++){ scanf("%s",s); if(dp()) cnt++; } printf("%d/n",cnt); } return 0; } /* input: **??*?***? 6 a ab abc abcd abcde abcdef output: 3 */

F题:贪心题。一开始,我把这道题定格成了动归题,够早了很久也没能构造出来状态转移方程。后来想到数据都很小,想到了去模拟装箱的过程。贪心的策略是:拿到一个箱子,想装最大的,大的装不下的时候再去装小的,直到该箱子连最小的都装不下或者最小的都已装完为止。比如:一个6*6的箱子最多装一个6*6的物品就能恰好装满,这当然是最优的装箱方法。接着6*6的物品装完后,装5*5的物品,由于一个6*6的箱子最多容纳一个5*5的物品,这样的话,有多少5*5的物品就需要多少6*6的箱子。是不是这样就可以了呢?当然不是的,每个装5*5的箱子,都还能容纳11(6*6-5*5)个1*1的物品,尽可能多地将1*1的物品放进去就是最有策略。其它的物品,贪心策略类似。

           #include<iostream> using namespace std; int num[7]; int main(){ while(cin>>num[1]){ int total=num[1]; for(int i=2;i<=6;i++){ cin>>num[i]; total+=num[i]; } if(!total) break; int cnt=0; //开始装6*6的物品 cnt+=num[6]; //6*6物品装箱结束 cnt+=num[5]; //开始装5*5的物品 int left=num[5]*11; if(left>=num[1]) num[1]=0; else num[1]-=left; //5*5的物品装箱结束 cnt+=num[4]; //开始装4*4的物品 left=num[4]*5; if(left>=num[2]){ left=(left-num[2])*4; num[2]=0; if(left>=num[1]) num[1]=0; else num[1]-=left; } else num[2]-=left; //4*4的物品装箱结束 cnt+=num[3]/4; //开始装3*3的物品 cnt++;num[3]%=4; int a[4]={0,5,3,1}; left=a[num[3]]; if(left>=num[2]){ left=36-num[3]*9-num[2]*4; num[2]=0; if(left>=num[1]) num[1]=0; else num[1]-=left; } else{ num[2]-=left; left=36-num[3]*9-left*4; if(left>=num[1]) num[1]=0; else num[1]-=left; } } //3*3的物品装箱结束 cnt+=num[2]/9; //开始装2*2的物品 if(num[2]%9){ cnt++;num[2]%=9; left=36-4*num[2]; if(left>=num[1]) num[1]=0; else num[1]-=left; } //2*2的物品装箱结束 cnt+=num[1]/36; //最后装1*1的物品 if(num[1]%36) cnt++; cout<<cnt<<endl; } return 0; }

G题:计算几何+动态规划。这道题没有做出来还是有原因的,是个通病。就像做E题那样,想当然地把题目定格成了KMP,就束手就擒了。这道题看起来就是计算几何的题目,k个顶点一定是在凸包上,但是如何枚举这k个点,就认为是更高深的计算几何算法而退却了。看了解题报告才知道,其实使用DP来求最大面积的。定义f[i][j][k]为凸包上从下标为i的顶点逆时针方向到下标为j(i,j包含在内)的点中选取k个点作为多边形时的最大面积,容易得出:f[i][j][k]=max{f[i][p][k-1]+area(i,p,j)},其中p为与i,j之间。时间复杂度为O(n^4)。但是本题有一个细节:当凸包上的定点数目小于k的时候,所求结果直接为凸包的面积,而不能采用DP求解!

        #include<iostream> #include<cmath> using namespace std; typedef struct Node{ double x,y,ang; }Point; Point p[62],ch[62]; void get_ang(int n){ for(int i=1;i<n;i++) p[i].ang=atan2(p[i].y-p[0].y,p[i].x-p[0].x); } double dis(Point a,Point b){ return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)); } bool cmp(Point a,Point b){ if(a.ang!=b.ang) return a.ang<b.ang; if(a.y!=b.y) return a.y>b.y; double d1=dis(a,p[0]),d2=dis(b,p[0]); return d1<d2; } double cross(Point a,Point b,Point o){ return (a.x-o.x)*(b.y-o.y)-(b.x-o.x)*(a.y-o.y); } int convex(int n){//求凸包并返回凸包大小 int pos=0; for(int i=1;i<n;i++) if(p[i].x<p[pos].x || (p[i].x==p[pos].x && p[i].y<p[pos].y)) pos=i; Point tmp; tmp=p[0];p[0]=p[pos];p[pos]=tmp; get_ang(n); sort(p+1,p+n,cmp); int top=1; ch[0]=p[0];ch[1]=p[1]; for(int i=2;i<n;i++){ while(top>0 && cross(ch[top],p[i],ch[top-1])<=0) top--; ch[++top]=p[i]; } if(cross(ch[top],ch[0],ch[top-1])==0.0) top--; return top+1; } double area(int n){ //求n顶点的多边形面积 double s=0.0; for(int i=1;i<n-1;i++) s+=cross(ch[i],ch[i+1],ch[0]); return fabs(s/2.0); } int main(){ int t,n,k; cin>>t; while(t--){ cin>>n>>k; for(int i=0;i<n;i++) cin>>p[i].x>>p[i].y; if(n<3 || k<3) cout<<"0.00"<<endl; else{ int cp=convex(n),num=2*cp; if(cp<=k) printf("%.2lf/n",area(cp));//细节! else { for(int i=cp;i<num;i++) ch[i]=ch[i-cp]; //这是便于枚举做的预处理 double f[31][62][11]; for(int i=0;i<cp;i++) for(int j=0;j<num;j++) for(int r=0;r<=k;r++) f[i][j][r]=0.0; double res=0.0; for(int i=0;i<cp;i++) for(int r=3;r<=k;r++) for(int j=i+r-1;j-i<=cp;j++){ double tt=0.0; for(int p=i+r-2;p<j;p++){ double tmp=f[i][p][r-1]+fabs(cross(ch[i],ch[p],ch[j])*0.5); if(tmp>tt) tt=tmp; } f[i][j][r]=tt; if(r==k && f[i][j][r]>res) res=f[i][j][k]; } printf("%.2lf/n",res); } } } return 0; }

H题:RMQ+Union Find Set。这道题是个基本的图论题目,就是判断无向图中任意两个顶点是否连通的问题。RMQ问题一般有两种方法来实现:ST算法和Segment Tree,后来我试了一下,两种算法都是可以过的。就这道题而言,这是一个静态查询的题目,也即相应结点的信息没有动态地进行修改,采用RMQ来实现更高效些,ST算法的复杂度为O(n*logn)的预处理+O(1)的查询,比线段树高效些。

        #include<iostream> #include<cmath> # define Max 100001 using namespace std; int index[Max],node[Max],fs[Max][31],fb[Max][31]; int set[Max],rank[Max]; void Init(int n){ //幷查集初始化 for(int i=1;i<=n;i++){ set[i]=i;rank[i]=0; } } int find_set(int x){ //查找 if(x-set[x]) set[x]=find_set(set[x]); return set[x]; } void Union(int x,int y){ // 合并 x=find_set(x);y=find_set(y); if(rank[x]>rank[y]) set[y]=x; else{ if(rank[x]==rank[y]) rank[y]++; set[x]=y; } } void rmq(int n){ //ST算法高效求解静态RMQ问题 for(int i=1;i<=n;i++) fs[i][0]=fb[i][0]=node[i]; int lmt=(int)(log10(n*1.0)/log10(2.0)); for(int i=1;i<=lmt;i++) for(int j=1;j+(1<<i)-1<=n;j++){ fs[j][i]=min(fs[j][i-1],fs[j+(1<<(i-1))][i-1]); fb[j][i]=max(fb[j][i-1],fb[j+(1<<(i-1))][i-1]); } } int get_max(int s,int t){ int k=(int)(log10((t-s+1)*1.0)/log10(2.0)); return max(fb[s][k],fb[t-(1<<k)+1][k]); } int get_min(int s,int t){ int k=(int)(log10((t-s+1)*1.0)/log10(2.0)); return min(fs[s][k],fs[t-(1<<k)+1][k]); } int main(){ int n,c=1; while(scanf("%d",&n)!=-1){ for(int i=1;i<=n;i++){ scanf("%d",&node[i]); index[node[i]]=i; } printf("CASE %d/n",c++); rmq(n); int m,s,t,x,y; Init(n); scanf("%d",&m); for(int i=0;i<m;i++){ scanf("%d%d",&s,&t); x=get_min(s,t);y=get_max(s,t); Union(x,y); } int k,u,v; scanf("%d",&k); for(int i=0;i<k;i++){ scanf("%d%d",&u,&v); if(find_set(u)==find_set(v)) printf("YES/n"); else printf("NO/n"); } } return 0; }

 

 

 

 

你可能感兴趣的:(算法,数据库,struct,tree,ini,语言)