程序设计思维与实践第十一十二周实验小结

本文总结了程序设计课程11-12周的实验内容,包括房子购买计算、方阵旋转判断、解密算法、序列平衡序列查找、现金支付优化、听歌方案选择等题目,以及模拟题中的序列分段和棋盘消除问题,探讨了解题策略和算法应用。
摘要由CSDN通过智能技术生成

11周作业
A题
房子价格是 200万,以每年百分之 K增长,每年积攒 N万,问第几年能够买下这套房子。
第一年积攒N万,房价200万;第二年积攒2N万,房价200(1+K)万,第三年积攒3N万,房价200(1+K)^2 万 ……每一年都比较积攒数与房子价格,当积攒数大于等于房子价格时,为可以购买。当超过限定时间时视为不可购买。

#include<iostream> 

using namespace std; 
  int main(){
  int N;
  double K;
  double price=200.0;
  int year=1,flag=0;
  cin>>N>>K;
  K=K/100.00;
  while(flag==0&&year<=20){
  	if(price<=year*N){flag=1;}
  	else {price=price+price*K;year++;}
  }
  if(flag==1){cout<<year<<endl;}
  else        {cout<<"Impossible"<<endl;}
  return 0;}

B题
n^2个同学排列成一个 n∗n的方阵,用 0 表示男生,用 1表示女生。给定两种方阵方案,判断前一种方阵能否旋转为后一种方阵。
不需要翻转:前一方阵的任意位置的值都与后一方阵对应位置的值相等。
顺时针旋转九十度:前一方阵第i行第j个元素与后一方阵第n-1-i列第j个元素对应相等。
顺时针旋转一百八十度:前一方阵第i行第j个元素与后一方阵第n-1-i行第n-j-1个元素对应相等。
顺时针旋转二百七十度:前一方阵第i行第j个元素与后一方阵第i列第n-j-1个元素对应相等。
不符合上述情况则不能旋转。

#include<iostream> 

using namespace std; 
int a[20][20];
int b[20][20];
  int main(){
  int n;
  cin>>n;
  for(int i=0;i<n;i++){
  	for(int j=0;j<n;j++){
  		cin>>a[i][j];
	  }
  }
  for(int i=0;i<n;i++){
  	for(int j=0;j<n;j++){
  		cin>>b[i][j];}}
  	int x=0;
  for(int i=0;i<n;i++){
  	for(int j=0;j<n;j++){
  		if(a[i][j]!=b[i][j]){
  			x=-1;
  			break;
		  }}}
  if(x==-1){
  	x=1;
  	for(int i=0;i<n;i++){
  	  for(int j=0;j<n;j++){
  		if(a[i][j]!=b[j][n-1-i]){
  			x=-1;
  			break;
		  }}}
  }
  if(x==-1){
  	x=2;
  	for(int i=0;i<n;i++){
  	  for(int j=0;j<n;j++){
  		if(a[i][j]!=b[n-1-i][n-1-j]){
  			x=-1;
  			break;
		  }}}
  }
  if(x==-1){
  	x=3;
  	for(int i=0;i<n;i++){
  	  for(int j=0;j<n;j++){
  		if(a[i][j]!=b[n-1-j][i]){
  			x=-1;
  			break;
		  }}}
  }
  cout<<x<<endl;
  return 0;}

C题
给定明文密文对应规则,对密文解密得到明文。
由题目所给对应规则可知,对于大部分明文字母,其密文是其之后第五个字母。之所以是大部分,是因为V-Z不存在之后五位的字母。我们按照次序给其进行A-E的密文分配。由此我们得知明文密文对应规则为密文=第(明文+5)%26位字母,当密文大于明文时,明文=第(密文-5)%26位字母,反之,明文=第(密文+26-5)%26位字母。由对应规则得到明文。

#include<iostream> 
#include<string>
#include<string.h>
#include<stdio.h>
using namespace std; 

  int main(){
  	string s;
  	getline(cin,s);
  	for(int i=0;i<s.length();i++){
  		if(s[i]>='A'&&s[i]<='E'){
  			s[i]=s[i]+26-5;
		  }
		else if(s[i]>='F'&&s[i]<='Z'){
			s[i]=s[i]-5;
		}
	  }
	cout<<s<<endl;
  }

D题
找到一段最长的序列,该序列中1的数量等于2的数量,且前一半全是一种,后一半全是另外一种。
对于任意一段全为1的序列,它可以与其之前一段全为2的序列组合,也可以与其之后一段全为2的序列组合;对于全为2的序列也是同样的道理。由一段全为1的序列和一段全为2的序列组成的合法序列的最大长度取决于两段序列中长度较小者。例如,111122最长合法序列长度为4,为序列2的两倍。因此,我们每找到一种组合,就由该组合下较短序列的长度确定组合中合法序列的最大长度。遍历所有组合,我们便找到了最长的合法序列。

#include<iostream> 

using namespace std; 

  int main(){
  	int n;
  	cin>>n;
  	int sum1=0,sum2=0,num=0,max=0,pre=0;
  	for(int i=0;i<n;i++){
  		cin>>num;
		if(num==1){
			if(pre!=2){sum1++;}
			else {
				if(sum1==0){sum1++;}
				else{
					if(sum1>=sum2){if(max<2*sum2) max=2*sum2;}
					else if(sum1<sum2){if(max<2*sum1) max=2*sum1;}
					sum1=1;
				}}}
		if(num==2){
			if(pre!=1){sum2++;}
			else {
				if(sum2==0){sum2++;}
				else{
					if(sum1>=sum2){if(max<2*sum2) max=2*sum2;}
					else if(sum1<sum2){if(max<2*sum1) max=2*sum1;}
					sum2=1;
				}}}
		pre=num;	
	  }
	  if(sum1>=sum2){if(max<2*sum2) max=2*sum2;}
	  else if(sum1<sum2){if(max<2*sum1) max=2*sum1;}
	cout<<max<<endl;
  }

E题
给定所请求的现金量 纸币面额和该纸币的数量。输出不超过现金量的最大可支付数。
由于给定纸币面额的同时,限定了该纸币的数量。所以这道题是一道典型的多重背包问题,将不同纸币的数量进行二进制拆分转换为0,1背包问题来解决。为节约空间,0,1背包采用逆序。

#include<iostream>
#include<cstring>

using namespace std;

int cash,N,n[11],d[11],c[10001],s[100010];

int main(){
    while(cin>>cash){
    	cin>>N; 
        for (int i=0;i<N;i++)
            cin>>n[i]>>d[i];
        int cnt=0;
        for (int i=0;i<N;i++){
            int t=n[i];
            for (int k=1;k<=t;k<<=1){
                c[cnt]=k*d[i];
                t-=k;
                cnt++;
            }
            if(t>0){
                c[cnt]=t*d[i];
                cnt++;
            }
        }
        for(int i=0;i<cnt;i++)//普通01
            for(int j=cash;j>=c[i];j--)
                s[j]=max(s[j],s[j-c[i]]+c[i]);
        cout<<s[cash]<<endl;
        memset(s,0,sizeof(s));
    }

    return 0;
}

F题
给定开车时间,唱片数量和每一张唱片的播放长度。输出总时长不超过开车时间的听歌方案与时长。
由于开车时间相对较长,而唱片数量相对较少,我将这道题视为超大背包来解决。而由于唱片数量足够小,所以无需进行拆分,所以简单用枚举子集的办法就可以得到最长时长和此时的方案。

#include<iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;


void dfs(int num,int *a,int *b,int *c,int N,int M,int sum,int flag,int &max){
	if(flag==0&&sum<=N){
		
	if(num<M){
	b[num]=0;
	dfs(num+1,a,b,c,N,M,sum,flag,max);
	
	b[num]=1;
	sum=sum+a[num];
	dfs(num+1,a,b,c,N,M,sum,flag,max);}
	else {
		if(max<sum&&sum<=N){
		max=sum;
		for(int i=0;i<M;i++){c[i]=b[i];}
		flag=1;}
	}}}
int main(){
	int N;
	while(scanf("%d",&N)!=EOF){
		int M;
		cin>>M;
		int a[M];
		int b[M];
		int c[M];
		for(int i=0;i<M;i++){
			cin>>a[i];
			b[i]=0;
		}
	int sum=0,flag=0,max=0;
	dfs(0,a,b,c,N,M,sum,flag,max);
	for(int i=0;i<M;i++){
		if(c[i]==1){
			cout<<a[i]<<" ";
		}
	}
	cout<<"sum:"<<max<<endl;
}} 

11周模拟题
T1
给定一个序列,输出序列的段数。(段定义:连续的相同的最长整数序列)
从第二个元素开始遍历,如果与前一位置元素相同,证明在同一段中,段数不变。反之,则在不同段中,段数加一,遍历完毕即得到最终段数。

#include<iostream> 

using namespace std;

int a[1010];

int main()
{
    int n;
    cin>>n;
    int num=1;
    for(int i=0;i<n;i++){cin>>a[i];}
    for(int i=1;i<n;i++)
    {
    	if(a[i]!=a[i-1]){
    		num++;
		}}
        cout<<num<<endl;
    }

T2
给定一个棋盘,一个格子一种颜色。当一行或一列上有连续三个或者更多相同颜色的棋子时,颜色消除(一个棋子可以在某一行和某一列同时被消除),输出消除后的棋盘。
消除:对于棋盘任何一行或一列,我们记录在该行或该列中连续的颜色相同的棋子数量。若数量不小于3,则对这些棋子进行颜色消除。为避免行的消除对列的影响或列的消除对行的影响,我们将行与列的消除分开进行,只要消除过一次,则颜色就被消除。

#include<iostream> 

using namespace std;

int a[35][35];
int b[35][35];

int main()
{
    int n,m;
    cin>>n>>m;
    int sum=1;
    for(int i=0;i<n;i++){
    	for(int j=0;j<m;j++){
    		cin>>a[i][j];
    		b[j][i]=a[i][j];
		}
	}
    for(int i=0;i<n;i++){
    	for(int j=1;j<m;j++){
    		if(a[i][j]==a[i][j-1]){
    			sum++;
    			if(j==m-1){
    				if(sum>2){
    					for(int k=0;k<sum;k++){a[i][j-k]=0;}}
				sum=1;}
			}
			else{
				if(sum>2){
					for(int k=1;k<=sum;k++){
						a[i][j-k]=0;}}
			sum=1;}
		}
	}
	for(int i=0;i<m;i++){
    	for(int j=1;j<n;j++){
    		if(b[i][j]==b[i][j-1]){
    			sum++;
    			if(j==n-1){
    				if(sum>2){
    					for(int k=0;k<sum;k++){b[i][j-k]=0;}}
				sum=1;}
			}
			else{
				if(sum>2){
					for(int k=1;k<=sum;k++){
						b[i][j-k]=0;}}
			sum=1;}
		}
	}
	for(int i=0;i<n;i++){
    	for(int j=0;j<m;j++){
    		if(a[i][j]==0||b[j][i]==0){cout<<"0";}
			else{cout<<a[i][j];}
			if(j<m-1){cout<<" ";}}
		if(i<n-1) cout<<endl;
	}
    }

T4
给定一个字符串,计算字符串中delicious子串的数量。delicious子串定义:当且仅当每一个字符都属于一个大于1的回文串。
根据题意,delicious串分为两种:回文串和由回文串连接构成的字符串。如果按照这个思路固然可以算出所有delicious串,但在字符串长度极大时情况无疑会十分复杂。我们不妨反过来思考:什么样的子串不是delicious串?经过分析:子串的第一个元素为一种,剩余元素为一种与子串最后一个元素为一种,剩余元素为一种的情况才不构成deliciou串。由此,我们可以找出1所有非delecious串,由总串数减去非delecious串数,我们就得到了所有delicious串数。

#include<iostream>
#include<string>
using namespace std;

int main()
{   int n;
    cin>>n;
    string s;
    cin>>s;
    long long int ans;
	ans=1ll*(n-1)*n/2;
	int p=0;
	int num=0;
	for(int i=1;i<n;i++){
	    if(s[i-1]!=s[i]){
		    ans=ans-(i-p);
		    p=i;}}
	p=0;
	for(int i=1;i<n;i++)
	if(s[i-1]!=s[i])
	{   p=i-1;
		while(s[i]==s[i+1]){
			i++;
		}
		ans=ans-(i-p);
		num++;
	}
	ans=ans+num;
	cout<<ans;
	return 0;
}

12周
A题
给出n个数,找出出现至少(n+1)/2次的数。
对于输入的序列我们先进行排序(保证相同的数位置相邻)。从第一个位置开始遍历,找出每一个数的数量,数量不小于(n+1)/2直接输出,反之继续查找。

#include<iostream>
#include<algorithm>
using namespace std;

int a[1000001]; 
int main(){
    int N;
    while(cin>>N){
    	for(int i=0;i<N;i++){
    		cin>>a[i];
		}
	sort(a,a+N);
	a[N]=-1;
	int sum=1;
	for(int i=0;i<N;i++){
		if(a[i]==a[i+1]){sum++;}
		else{
			if(sum>=(N+1)/2){ cout<<a[i]<<endl;break;}
			sum=1;}
	}}
    
    return 0;
}

B题
三维迷宫寻找路径,每次只能向上下左右前后移动,判断路径是否存在及走出迷宫消耗时间。
三维迷宫最短路径问题,与二维迷宫相似,从起点开始进行广度优先搜索。到达终点时的路径长度即为最小时间。

#include<iostream>
#include<queue>
using namespace std;

struct point
{
    int z,x,y;
};

int space[30][30][30]; 
point pre[30][30][30];
int dxyz[6][3]={{0,0,1},{0,0,-1},{0,1,0},{0,-1,0},{1,0,0},{-1,0,0}};
int main(){
    int L,R,C;
    char c;
    point s,e,q;
    while(cin>>L>>R>>C&&L!=0&&R!=0&&C!=0){
    	for(int i=0;i<L;i++){
    		for(int j=0;j<R;j++){
    			for(int k=0;k<C;k++){
    	            cin>>c;
					if(c=='#'){space[i][j][k]=1;}
					else if(c=='.'){space[i][j][k]=0;}
					else if(c=='S'){space[i][j][k]=0;s.z=i;s.x=j;s.y=k;}
					else if(c=='E'){space[i][j][k]=0;e.z=i;e.x=j;e.y=k;}}}}
	queue<point> Q;
	Q.push(s);
	space[s.z][s.x][s.y]=1;
	bool flag=false;				
	 while(!Q.empty()&&!flag)
    {   
        for(int i=0;i<6;i++)
        {
            q.x=Q.front().x+dxyz[i][1];
            q.y=Q.front().y+dxyz[i][2];
            q.z=Q.front().z+dxyz[i][0];
            if(q.x>=0&&q.x<R&&q.y>=0&&q.y<C&&q.z>=0&&q.z<L&&space[q.z][q.x][q.y]!=1)
            {
                Q.push(q);
                space[q.z][q.x][q.y]=1;
                pre[q.z][q.x][q.y]=Q.front();
                if(q.z==e.z&&q.x==e.x&&q.y==e.y)
                {
                	flag=true;
                    break;
                }}}
        Q.pop();
    }
    if(flag){
	int sum=0;
        while(q.z!=s.z||q.x!=s.x||q.y!=s.y)
        {
            int z=q.z,x=q.x,y=q.y; 
            q.z=pre[z][x][y].z;
            q.x=pre[z][x][y].x;
            q.y=pre[z][x][y].y;
            sum++;
        }
	cout<<"Escaped in "<<sum<<" minute(s)."<<endl;}
    else {cout<<"Trapped!"<<endl;}}
	return 0;}
    

C题
每个寝室里面有ai个人。从第i到第j个宿舍一共有sum(i,j)个人,找到sum(i1, j1) + … + sum(im,jm)的最大值。且m段都不能相交。
无论到哪一个宿舍,都会出现两种可能:一是与其他宿舍一起组成其中的一段,另一种是另起新的一段。我们取其中的最大值作为到达该宿舍的最大值。重复m次,对每个最大值进行遍历,我们就得到了m次加法中的最大值。

#include<iostream> 
#include<stdio.h>
#include<string.h>
#include<math.h>

using namespace std;

int a[1000010];
int dp[1000010];
int s[1000010];

int main()
{
    int m,n;
    while(cin>>m>>n)
    {   memset(dp,0,sizeof(dp));
        memset(s,0,sizeof(s));
        int sum=0;
        for(int i=1;i<=n;i++){scanf("%d",&a[i]);}
        for(int i=1;i<=m;i++)
        {
                sum=-9999999;
                for(int j=i;j<=n;j++)
                {
                    dp[j]=max(dp[j-1]+a[j],s[j-1]+a[j]);
                    s[j-1]=sum;
                    sum=max(sum,dp[j]);
                }
        }
        cout<<sum<<endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值