您的位置:首页 > 其它

2016.3.20的解题报告

2016-05-20 15:55 155 查看
今天,我们做了五道题,可我最后只拿了52分。可以看出,我的编程技术还不够扎实,做题时需要从整体考虑。下面上题解。1、看电影你们宿舍有N个人去看电影,你们决定所有人要坐在同一行或所有人在同一列,但是不一定要坐在相邻的位置。电影院有R行C列的座位,‘#’表示某个座位已经被订了,‘.’表示该座位为空。问你们有多少种不同的坐法?只要有一个人坐的位置不同都认为是不同的坐法。输入格式:483_5srm.in多组测试数据。第一行:一个整数tt,表示共有tt组测试数据。1<=tt<=5.每组测试数据的格式如下:第一行,三个整数N,R,C,1<=N<=8,1<=R,C<=50。接下来有R行C列的字符。每个是‘#’或者‘.’。输出格式:483_5srm.out共tt行。每行一个整数,表示N个人不同的坐法。
样例输入:2234.#...##.....233..#.##...样例输出:3416
这题其实是很简单的一道题目,可是我只对了21分。【题意分析】题目说,全部人要在同一行或同一列上,那么,只要分别计算每行或每列中空的座位总数记为sum就可以了。然后在这sum的总数里,要选n个座位(可以无序)——那么这不就是排列组合吗?给出公式:如此,只要按照以上公式计算P(sum,n)就可以了,注意,sum>=n。最后,加上所有的P(sum,n)。恭喜你,到了这里,你就能拿到1/4的分数了。(那么,剩下的3/4又去了哪里呢?)下面,来看一看一些坑人的数据。
输入样例1输入样例2
1 11.1 22....
正确输出1正确输出2
14
如果按照以上方法,则会分别输出2,8。那么,应该怎样做呢?
很明显,这样计算,每个点就会计算到两次(行和列分别算了两次)。那么,只要除以二就可以了!
好了,现在我们已经100分了,下面上程序(注意用longlong)。
#include<cstdio>#include<cstring>usingnamespacestd;charch;inttt;intn,r,c;inta[55][55];longlongp;longlongans,sum;voidinit(intr,intc){for(inti=1;i<=r;i++){scanf("\n");for(intj=1;j<=c;j++){ch=getchar();if(ch=='.')a[i][j]=1;elsea[i][j]=0;}}}longlongP(intx,inty){if(x<y)return0;p=1;for(intk=x-y+1;k<=x;k++)p*=k;returnp;}intmain(){freopen("483_5srm.in","r",stdin);freopen("483_5srm.out","w",stdout);scanf("%d",&tt);for(intt=1;t<=tt;t++){memset(a,0,sizeof(a));ans=0;scanf("%d%d%d",&n,&r,&c);init(r,c);for(inti=1;i<=r;i++){sum=0;for(intj=1;j<=c;j++)if(a[i][j])sum++;if(sum>=n)ans+=P(sum,n);}for(intj=1;j<=c;j++){sum=0;for(inti=1;i<=r;i++)if(a[i][j])sum++;if(sum>=n)ans+=P(sum,n);}if(n==1)ans>>=1;printf("%I64d\n",ans);}return0;}
2、汉诺塔(2秒)汉诺塔问题你一定很熟悉了吧,有三根柱子source、target、mid,有N个半径不同的盘子一开始套在A柱子上,从下往上看盘子的半径是递减的。现在你通过移动盘子,使得N个盘子最终全部在柱子C上,从下往上看盘子的半径还是递减的。每次你只能移动一个盘子,而且任意时刻都不能出现大的盘子在上小的盘子在下。现在Dave和Earl玩这个游戏,Dave的程序是想用尽量少的移动次数来完成目的,他只需移动2^N-1次盘子;而Earl的程序是保证移动过程中三根柱子上面的盘子的状态是唯一的,因此需要3^N-1次移动。下面是他们两人的程序:Solve_Dave(source,target,mid,N){If(N>0){Solve_Dave(source,mid,target,N-1);moveadiskfromsourcetotargetSolve_Dave(mid,target,source,N-1);}}Solve_Earl(source,target,mid,N){If(N>0){Solve_Earl(source,target,mid,N-1);moveadiskfromsourcetomidSolve_Earl(target,source,mid,N-1);moveadiskfrommidtotargetSolve_Earl(source,target,mid,N-1);}}给出一个整数N,表示盘子的数量,另外给出一个整数K,表示Dave按照他自己的算法已经移动了K次盘子,请问Earl按照他自己的算法要移动多少次盘子,才能使得三根柱子的状态和Dave的状态是完全相同的?输入格式:482_2srm.in多组测试数据。第一行:一个整数tt,表示共有tt组测试数据。1<=tt<=5.每组测试数据的格式如下:一行,两个整数N和K。1<=N<=19,0<=K<=2^N-1。输出格式:482_2srm.out共tt行,每行一个整数,Earl按照他自己的算法要移动盘子的次数。输入样例:231415输出样例:280
【题意分析】
这是本套题中较难的一道题。下面上题解:
先来看一看n=4时,Dave(即要移动2^n-1次数的那一个)移动中和Earl(移动3^n-1次数的那一个)移动中都有出现的情况:
下面:是A柱={1,3};b柱={2,4};c柱为空时,Earl移动时的特例:
3^n-11234ABC次数:ans+=01243ABC次数:ans+=3^(4-1)*11342ABC次数:ans+=3^(3-1)*21234ABC次数:ans+=3^(2-1)*1
可以看出,当n移动从x柱移动到y柱时,需要3^(n-1)*|y-x|次。那么,我们只要把Dave移动了第k个时的状态记录下来,然后按照上面的思路就可以了。
#include<cstdio>#include<cstring>#include<cmath>usingnamespacestd;intf[4][25];intlen[4];intt[25],t1[25];intd;inttt;intn,m;voidchange(inta,intb){f[++len[b]]=f[a][len[a]--];f[a][len[a]+1]=0;}voidmove(inta,intb,intc,intn){if(n==0||d>m)return;move(a,c,b,n-1);if(++d>m)return;change(a,b);move(c,b,a,n-1);}intcount(){intans=0;memset(t,0,sizeof(t));memset(t1,0,sizeof(t1));for(intj=1;j<3;j++)for(inti=1;i<=len[j];i++)t[f[j][i]]=j;for(inti=n;i>=1;i--){if(t1[i]!=t[i]){ans+=abs(t1[i]-t[i])*pow(3,i-1);t1[i]=t[i];if(t1[i]==1){for(intj=1;j<i;j++)if(t1[j]==0)t1[j]=2;elset1[j]=0;}}}returnans;}intmain(){freopen("482_2srm.in","r",stdin);freopen("482_2srm.out","w",stdout);scanf("%d",&tt);for(;tt>=1;tt--){memset(f,0,sizeof(f));memset(len,0,sizeof(len));scanf("%d%d",&n,&m);len[0]=n;len[1]=len[2]=0;d=0;for(inti=n;i>=1;i--)f[0][n-i+1]=i;move(0,2,1,n);printf("%d\n",count());}return0;}

1.10关路灯

源程序名power.???(pas,c,cpp)可执行文件名power.exe输入文件名power.in输出文件名power.out
[b]【问题描述】某一村庄在一条路线上安装了n盏路灯,每盏灯的功率有大有小(即同一段时间内消耗的电量有多有少)。老张就住在这条路中间某一路灯旁,他有一项工作就是每天早上天亮时一盏一盏地关掉这些路灯。为了给村里节省电费,老张记录下了每盏路灯的位置和功率,他每次关灯时也都是尽快地去关,但是老张不知道怎样去关灯才能够最节省电。他每天都是在天亮时首先关掉自己所处位置的路灯,然后可以向左也可以向右去关灯。开始他以为先算一下左边路灯的总功率再算一下右边路灯的总功率,然后选择先关掉功率大的一边,再回过头来关掉另一边的路灯,而事实并非如此,因为在关的过程中适当地调头有可能会更省一些。现在已知老张走的速度为1m/s,每个路灯的位置(是一个整数,即距路线起点的距离,单位:m)、功率(W),老张关灯所用的时间很短而可以忽略不计。请你为老张编一程序来安排关灯的顺序,使从老张开始关灯时刻算起所有灯消耗电最少(灯关掉后便不再消耗电了)。【输入】文件第一行是两个数字n(0<n<50,表示路灯的总数)和c(1<=c<=n老张所处位置的路灯号);接下来n行,每行两个数据,表示第1盏到第n盏路灯的位置和功率。【输出】一个数据,即最少的功耗(单位:J,1J=1W·s)。【样例】power.inpower.out53270{此时关灯顺序为34215,不必输出这个关灯顺序}210320520630810
这题是挺简单的一道题,可是,我在做题的时候却没有想到。首先,他经过的某盏灯,假如这盏灯还是亮着的,那么他不可能不把它关掉,因为他关灯是不需要时间的。所以,他关完的灯肯定是一段连续的区间。既然如此,我们就可以动归,可以动归就可以搜索。f[i][j][k]表示i,j这一段区间已经关完灯了,且他在第k盏灯上。
以下是记忆化搜索的程序:
#include<cstdio>#include<iostream>#include<algorithm>#include<cstring>usingnamespacestd;constintINF=1<<29;intf[60][60][60];intdis[60];intsum[60];intn,m,x;inlineintfind(intl,intr,intlast){if(l==1&&r==n)return0;if(f[l][r][last]!=-1)returnf[l][r][last];intans1=INF,ans2=INF;if(l>1){ans1=find(l-1,r,l-1)+(dis[last]-dis[l-1])*(sum-sum[r]+sum[l-1]);}if(r<n){ans2=find(l,r+1,r+1)+(dis[r+1]-dis[last])*(sum-sum[r]+sum[l-1]);}returnf[l][r][last]=min(ans1,ans2);}intmain(){freopen("power.in","r",stdin);freopen("power.out","w",stdout);scanf("%d%d",&n,&m);memset(f,-1,sizeof(f));for(inti=1;i<=n;i++){scanf("%d%d",&dis[i],&x);sum[i]=sum[i-1]+x;//前缀和,方便统计其他还没有关完灯的效率。}printf("%d\n",find(m,m,m));return0;}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: