模拟赛Day2补题报告

目录

一.题目报告

二.赛中概况

三.解题报告

T1.下棋(chess)

  题目情况

  题目大意

  题目解析

  题目正解

T2.汪洋(BigWater)

  题目情况

  题目大意

  题目解析

  题目正解

T3.删数(delnum)

  题目情况

  题目大意

  题目解析

题目正解

T4.平分糖果(candy)

  题目情况

  题目大意

  题目解析

  题目正解

四.总结


一.题目报告

  赛中第一题AC,二、三、四题0分。赛后全部AC。


二.赛中概况

  第一题贪心15分钟过,第二题首先使用dfs,之后又使用dp,结果不仅一分不得,还在这个题上浪费了过多时间,导致做三四题的时间太少,最终只拿了第一题的100分。


三.解题报告

T1.下棋(chess)

  题目情况

    赛中AC。

  题目大意

    共n位玩家,每位玩家有x个三星,y个两星,z个一星,三个一星合成一个两星,三个两星合成一个三星,每位玩家的实力为18x+3y+z,将玩家的实力从大到小排序,相同实力的序号小的排在前面,可以合成完后再排名。

  题目解析

    不难发现,一星合成两星对总实力没有影响,而两星合三星可以使总实力上升9点,且数据范围较小,所以只需无脑贪心模拟即可。

  题目正解

    接下来奉上正解

#include<bits/stdc++.h>
using namespace std;
struct str{
	long long a,x,y,z;
}m[100001];
long long n;
bool cmp(str i,str j){
	if(i.x*18+i.y*3+i.z!=j.x*18+j.y*3+j.z){
		return i.x*18+i.y*3+i.z>j.x*18+j.y*3+j.z;
	}
	else{
		return i.a<j.a;
	}
}
int main(){
	freopen("chess.in","r",stdin);
	freopen("chess.out","w",stdout);
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>m[i].z>>m[i].y>>m[i].x;
		m[i].a=i;
		if(m[i].z>=3){
			m[i].y+=m[i].z/3;
			m[i].z%=3;
		}
		if(m[i].y>=3){
			m[i].x+=m[i].y/3;
			m[i].y%=3;
		}
	}
	sort(m+1,m+n+1,cmp);
	for(int i=1;i<=n;i++){
		cout<<m[i].a<<' ';
	}
	fclose(stdin);
	fclose(stdout);
	return 0;
} 

T2.汪洋(BigWater)

  题目情况

    赛中0分,赛后AC。

  题目大意

    给定一个n*n的矩阵,每个格子里都有一个数字a(|a|<=100)为走到这个格子时增加或减少的心情值(心情值初始为100),从(1,1)出发,一开始向右走(即(1,2)),走法为:

     ·沿着上一步的方向继续移动

     ·进行顺时针 90 度转向,(注意不可以在同一个格子内连续转向)

     ·不再逛那些逛过的格子(除了起点)

    求回到(1,1)时的最大心情值。

  题目解析

    分析题目可知,路线一定为一个矩形,所以可以枚举矩形的长和宽,然后计算矩形四条边上的数的和sum,最终答案就是所有 sum 中的最大值加初始心情 ,而每次求和可以通过前缀和预处理。所以本题其实是一个二维前缀和问题。

  题目正解

    我打的dp(现在看看真的像石

#include<bits/stdc++.h>
using namespace std;
long long n,a[1001][1001],dp[1001][1001][4],s=0,cnt=-11451414;
long long dx[4]={0,1,0,-1},dy[4]={1,0,-1,0},bx[2]={-1,0},by[2]={0,1};
int main(){
	freopen("BigWater.in","r",stdin);
	freopen("BigWater.out","w",stdout);
	cin>>n;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			cin>>a[i][j];
		}
	}
	memset(dp,-11567,sizeof(dp));
	dp[1][2][0]=dp[1][2][1]=a[1][2];
	for(int x=1;x<=n;x++){
		for(int y=2;y<=n;y++){
			for(int l=0;l<4;l++){
				if(s==3){
					for(int k=0;k<=1;k++){
		                int xx=x+bx[k];
		                int yy=y+by[k];
						if(k==0&&l==2){
							dp[xx][yy][3]=max(dp[xx][yy][3],dp[x][y][2]+a[xx][yy]);
						}
						if(k==1&&l==3){
							s=0;
							dp[xx][yy][0]=max(dp[xx][yy][0],dp[x][y][3]+a[xx][yy]);
						}
						if(k==0&&l==3){
							dp[xx][yy][l]=max(dp[xx][yy][l],dp[x][y][l]+a[xx][yy]);
						}
						if(k==1&&l==0){
							s=0;
							dp[xx][yy][l]=max(dp[xx][yy][l],dp[x][y][l]+a[xx][yy]);
						}
					}
				}
				for(int k=s;k<=s+1;k++){
	                int xx=x+dx[k];
	                int yy=y+dy[k];
					if(k==l){
						dp[xx][yy][l]=max(dp[xx][yy][l],dp[x][y][l]+a[xx][yy]);
					}
					if(l+1==k){
						s++;
						dp[xx][yy][k]=max(dp[xx][yy][k],dp[x][y][l]+a[xx][yy]); 
					}
				}
			}
		}
	}
	for(int i=0;i<4;i++){
		cnt=max(cnt,dp[1][1][i]);
	}
	cout<<cnt+100;
	fclose(stdin);
	fclose(stdout);
	return 0;
} 

   正解

#include<bits/stdc++.h>
using namespace std;
long long n,r[1001][1001],c[1001][1001],a[1001][1001],ans=-1145141; 
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			cin>>a[i][j];
			r[i][j]=r[i][j-1]+a[i][j];
			c[i][j]=c[i-1][j]+a[i][j];
    	}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			ans=max(ans,r[i][j]+r[1][j]+c[i][j]+c[i][1]-a[1][1]-a[1][j]-a[i][1]-a[i][j]);
    	}
	}
	cout<<ans+100;
	return 0;
} 

T3.删数(delnum)

  题目情况

    赛中0分,赛后AC。

  题目大意

    一个数字集合,其中有1,2,3,4,5,6.......1145141919810100869....(很多正整数)

    再给你一个长度为 n 的数组a(1≤a​i​​≤10​9​​),每次操作将当前集合中第 a​1​​ 小、第 a​2​​ 小、……、第 a​n​​ 小的数同时移除

    最后t次询问,每次给出一个数x,判断x是否能被删去,若能,输出删除次数,若不能,输出0。

  题目解析

    我们需要考虑的是a[i]<x<a[i+1] 的位置。

    以样例:

    5
    1 3 9 14 20
    1
    114
    为例:
    x>20的时候,都是要删除5个数字,那么需要删除多少次5个数呢?
  同理,需要删除多少次4个数字呢?
  我们需要删除19次5个数字。之后x=19,不能再考虑删除20了。
  我们需要删除2次4个数字。之后x=11,不能再考虑删除14了。
  ....
  此时单次查询的时间复杂度为O(n) 。

题目正解

#include<bits/stdc++.h>
using namespace std;
long long n,t,x,a[1000];
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	cin>>t;
	while(t--){
		int ans=0,f=0;
		cin>>x;
		for(int i=n;i>=1;i--){
			if(a[i]<=x){
				ans+=(x-a[i])/i;
				x=a[i]+(x-a[i])%i;
				if(x>a[i]){
					x-=i;
					ans++;
				}
			}
			if(a[i]==x){
				f=1;
				cout<<ans+1<<endl;
				break;
			}
		}
		if(f==0){
			cout<<0<<endl;
		}
	}
	return 0;
} 

T4.平分糖果(candy)

  题目情况

    赛时0分,赛后AC。

  题目大意

    已知美味程度为 i 的糖果有 a[i] 个,请问能不能把糖果平分成美味程度之和相同的两部分。(0<i<=6)

  题目解析

    每个物品的数量有限,所以找出是否存在几个物品的价值与物品总价值的一半相同就可以了,可以抽象成多重背包。用dp[i][j]表示是否可以用i个糖果凑出j的甜度。

    对于第 i 种物品,都可以取 [0,a[i]] 个,假设当前取了k个,则其状态转移为:
dp[i][j] |= dp[i-1][j-k*i]

  题目正解

#include<bits/stdc++.h>
using namespace std;
long long a[7],m,dp[7][100001],cnt;
int main(){
	while(1){
		m=0;
		for(int i=1;i<=6;i++){
			cin>>a[i];
			m+=a[i]*i;
		}
		if(!m) break;
		cout<<"Collection #"<<++cnt<<":"<<endl;
		dp[0][0]=1;
		for(int i=1;i<=6;i++){
			for(int j=0;j<=a[i];j++){
				for(int k=j*i;k<=m/2;k++){
					dp[i][k]|=dp[i-1][k-j*i];
				}
			}
		}
		if(m%2==0&&dp[6][m/2]==1){
		    cout<<"Can be divided."<<endl<<endl;
		}
		else{
			cout<<"Can't be divided."<<endl<<endl;
		}
	}
	return 0;
} 

四.总结

    对时间把握的不是很好,思维能力还需锻炼,还需多加练习。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值