台州学院第十六届大学生程序设计竞赛题解

A题

'c’本就弱小(tzcoder8390)

思路

水题

#include<bits/stdc++.h>
using namespace std;

int main(){
	double a,b,c;
	cin>>a>>b>>c;
	if(c+a>b&&c+b>a) cout<<"Yes"<<endl;
	else cout<<"No"<<endl;
	return 0;
}

B题

XOR!(tzcoder8391)

思路

模拟题,难点在于怎么处理中间的符号以及切割出整数值

#include<bits/stdc++.h>
using namespace std;

int main(){
	int t;
	cin>>t;
	while(t--){
		string s;
		cin>>s;
		int cas;//中间的运算符是什么 
		int num_left=0,num_right=0;//num_left是式子左侧的值,num_right是式子右侧的值
		int now=0;//当前切割的数字 
		for(int i=0;i<s.size();i++){
			if(s[i]=='^'){
				num_left^=now;
				now=0;
			}
			else if(s[i]=='+'){
				num_right+=now;
				now=0;
			}
			else if(s[i]>='0'&&s[i]<='9'){
				now=now*10+s[i]-'0';
			}
			else{//此时必定是中间的运算符
				num_left^=now;//注意处理最后一个整数 
				now=0; 
				//注意用if写的话,情况3要写在情况0前,情况4要写在情况1前面 
				if(s[i]=='<'&&s[i+1]=='=') cas=3,i++;
				else if(s[i]=='>'&&s[i+1]=='=') cas=4,i++;
				else if(s[i]=='!') cas=5,i++;//注意这里下标多+1,否则会和后面的=情况重复 
				else if(s[i]=='<') cas=0;
				else if(s[i]=='>') cas=1;
				else if(s[i]=='=') cas=2;
			}
		}
		num_right+=now;//右侧的最后一个整数不要忘记处理
		int flag;
		switch(cas){
			case 0:flag=num_left<num_right;break; 
			case 1:flag=num_left>num_right;break; 
			case 2:flag=num_left==num_right;break; 
			case 3:flag=num_left<=num_right;break; 
			case 4:flag=num_left>=num_right;break; 
			case 5:flag=num_left!=num_right;break; 
		} 
		if(flag) cout<<"Yes"<<endl;
		else cout<<"No"<<endl;
	}
}

C题

乐乐的强迫症(tzcoder8392)

思路

01 01 01背包模型
a a a b b b,其实就是能不能凑出 b − a b-a ba的分数。
把分值看做容量,这就是一道 01 01 01背包的经典模型。
注意特判 a > b a>b a>b的情况

#include<bits/stdc++.h>
using namespace std;

int dp[40007];//dp[i]代表凑出分数i的最少题目数量 

int main(){
	int n,a,b;
	cin>>n>>a>>b;
	if(a>b){
		cout<<-1<<endl;
		return 0;
	}
	dp[0]=0;//初始情况,0分是可以凑出来的 
	int lim=b-a;
	for(int i=1;i<=lim;i++) dp[i]=1e9;
	while(n--){
		int x;
		cin>>x;
		for(int i=lim;i>=x;i--){
			dp[i]=min(dp[i],dp[i-x]+1);
		}
	}
	if(dp[lim]==1e9) cout<<-1<<endl;
	else cout<<dp[lim]<<endl;
}

D题

maomeng的数学魔鬼训练(tzcoder8393)

思路

贪心
从直觉上,我们肯定要把耗时最长的书和耗时最少的游戏安排在同一天
那么如果我们把书按照耗时从大到小排序,把游戏按照耗时从小到大排序,是否这样的顺序就一定是最优的呢?

答案是必然的,证明如下
比如我们尝试对第 i i i多耗时的书(定义为 x [ i ] x[i] x[i])和第 i i i少耗时的游戏(定义为 y [ i ] y[i] y[i])安排在同一天
如果 x [ i ] + y [ i ] > 24 x[i]+y[i]>24 x[i]+y[i]>24,则无法安排
那么我们尝试,肯定要把 x [ i ] x[i] x[i]或者 y [ i ] y[i] y[i]中的一个尝试缩小
如果我们尝试缩小 y [ i ] y[i] y[i],也就是说要取一个 j < i j<i j<i,尝试用 y [ j ] y[j] y[j]替代 y [ i ] y[i] y[i]
那么即使我们可以使得 x [ i ] + y [ j ] < = 24 x[i]+y[j]<=24 x[i]+y[j]<=24成立,但是造成的影响 x [ j ] + y [ i ] x[j]+y[i] x[j]+y[i]必定是 > 24 >24 >24的,因为 x [ j ] > x [ i ] x[j]>x[i] x[j]>x[i]

#include<bits/stdc++.h>
using namespace std;

int main(){
	int n;
	cin>>n;
	int a[1007],b[1007];
	for(int i=0;i<n;i++) cin>>a[i];
	for(int i=0;i<n;i++) cin>>b[i];
	sort(a,a+n);
	sort(b,b+n);
	for(int i=0;i<n;i++){
		if(a[i]+b[n-i-1]>24){
			cout<<"NO"<<endl;
			return 0; 
		}
	}
	cout<<"YES"<<endl;
}

E题

##回文自动机(tzcoder8394)

思路

d p + dp+ dp+矩阵快速幂加速
需要借助哈希计算
见注释

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=2e5+7;
const int mod=1000000007;
string s;
long long hash_pre[maxn*2],hash_suf[maxn*2];
long long ans[2];//ans[0]代表回文的情况数,ans[1]代表不回文的情况数 
long long mat[2][2];//关系转移矩阵 
long long temp_base=1;//截取下来的一半字符串最高位+1对应的权值 
int cnt=0;

void mul_mat(long long a[2][2],long long b[2][2]){
	//计算矩阵a*矩阵b
	long long c[2][2];
	memset(c,0,sizeof(c));
	for(int i=0;i<2;i++){
		for(int j=0;j<2;j++){
			for(int k=0;k<2;k++){
				c[i][j]=(a[i][k]*b[k][j]%mod+c[i][j])%mod;
			}
		}
	} 
	for(int i=0;i<2;i++){
		for(int j=0;j<2;j++){
			a[i][j]=c[i][j];
		}
	}
}

void qpow(long long a[2][2],long long p){
	long long ans[2][2];
	ans[0][0]=ans[1][1]=1;
	ans[0][1]=ans[1][0]=0;
	while(p){
		if(p&1) mul_mat(ans,a);
		mul_mat(a,a);
		p>>=1;
	}
	for(int i=0;i<2;i++){
		for(int j=0;j<2;j++){
			a[i][j]=ans[i][j];
		}
	}
}

int main(){
	string s;
	long long k;
	cin>>s>>k;
	int len=s.size();
	s=s+s;
	for(int i=0;i<len*2;i++) hash_pre[i+1]=(hash_pre[i]*10%mod+s[i]-'a')%mod;
	for(int i=len*2-1;i>=0;i--) hash_suf[i+1]=(hash_suf[i+2]*10%mod+s[i]-'a')%mod;
	for(int i=0;i<len/2;i++) temp_base=temp_base*10%mod;
//	for(int i=1;i<=len*2;i++){
//		cout<<hash_pre[i]<<' '<<hash_suf[i]<<' '<<i<<endl; 
//	}
	for(int i=1;i<=len;i++){
		//以第i个字符作为字符串开头的情况
		long long hash1,hash2;
		if(len&1){
			//奇数情况下,第一段是[i,i+len/2-1],第二段是[i+len/2+1,i+len-1] 
			hash1=(hash_pre[i+len/2-1]-hash_pre[i-1]*temp_base%mod+mod)%mod;
			hash2=(hash_suf[i+len/2+1]-hash_suf[i+len]*temp_base%mod+mod)%mod;
			//cout<<hash1<<' '<<hash2<<' '<<i<<'!'<<endl;
		} 
		else{
			//偶数情况下,第一段是[i,i+len/2-1],第二段是[i+len/2,i+len-1] 
			hash1=(hash_pre[i+len/2-1]-hash_pre[i-1]*temp_base%mod+mod)%mod;
			hash2=(hash_suf[i+len/2]-hash_suf[i+len]*temp_base%mod+mod)%mod;
		}
		if(hash1==hash2){
			cnt++;
		}
		if(i==1){
			if(hash1==hash2) ans[0]=1;
			else ans[1]=1;
		}
	}
	mat[0][0]=cnt-1;//从回文转移到回文有cnt-1种 
	mat[1][0]=cnt;//从非回文转移到回文有cnt种 
	mat[0][1]=len-cnt;//从回文转移到非回文有len-cnt种 
	mat[1][1]=len-cnt-1;//从非回文转移到非回文有len-cnt-1种 
//	for(int i=0;i<2;i++){
//		for(int j=0;j<2;j++){
//			cout<<mat[i][j]<<' ';
//		}
//		cout<<endl;
//	}
	qpow(mat,k);
//	for(int i=0;i<2;i++){
//		for(int j=0;j<2;j++){
//			cout<<mat[i][j]<<' ';
//		}
//		cout<<endl;
//	}
	//最后计算ans矩阵乘以mat矩阵即可 
	//ans[0]就是答案 
	cout<<(ans[0]*mat[0][0]%mod+ans[1]*mat[1][0]%mod)%mod<<endl;
} 

F题

狭路相逢勇者胜!(tzcoder8395)

思路

思维结论,无算法
需要先能观察推导出,当前这一轮的第i位选手,如果他获胜晋级,那么他在下一轮会是第 i / 2 i/2 i/2位选手。

在此基础上,再想到什么时候两个人会相遇,即两个人的序号差值为 1 1 1,并且小的那名选手的序号是偶数。

因此不断循环模拟晋级过程即可,一直到两个人相遇。复杂度 l o g ( n ) log(n) log(n)

#include<bits/stdc++.h>
using namespace std;

int main(){
    int n,a,b;
    cin>>n>>a>>b;
    if(a>b) swap(a,b);//注意题目没说a>b,不交换会导致下面的循环死循环
    int ans=1;
    while(a%2||b!=a+1){
        a>>=1;
        b>>=1;
        ans++;
    }
    cout<<ans<<endl;
}

G题

“TZOJ”(tzcoder8396)

思路

此题可以 n 2 n^2 n2复杂度枚举每个点,作为我们选择的正方形区域的最左上角。
然后如果直接 n n n复杂度枚举正方形区域的边长,借助二维前缀和 O ( 1 ) O(1) O(1)计算区域内的T,Z,O,J的个数。
这种写法的复杂度是 n 3 n^3 n3,会超时。

容易注意到,对于左上角固定某个位置的情况下,边长越长,越可能满足包含T,Z,O,J的这个条件,也就是存在一个值x,当边长>=x时,正方形区域都满足要求,也就是满足二分性。
因此我们可以二分正方形的边长,将复杂度降低到 O ( n 2 ∗ l o g n ) O(n^2*logn) O(n2logn)

不过这里还可以继续优化
我们以 [ i ] [ j ] [i][j] [i][j]作为正方形的左上角时,其实对于所有的 i − j = k , k i-j=k,k ij=k,k是一个常数,也就是说对于从左上角斜向右下角的斜 45 45 45度线上的所有矩形,我们是可以放在一起讨论的。
如果说从这个“线性”角度思考,很容易发现是满足双指针移动方向的单调性。
即以 [ i ] [ j ] [i][j] [i][j]作为左上角, [ t o i ] [ t o j ] [toi][toj] [toi][toj]作为正方形区域刚好包含T,Z,O,J时的右下角
对于 i − j = k , k i-j=k,k ij=k,k是一个常数时,明显 i i i j j j越大, t o i toi toi t o j toj toj也越大。

因此对于每个斜线情况,我们可以采用双指针的策略,将整体复杂度降低到 O ( n 2 ) O(n^2) O(n2)
注意此时我们枚举矩阵左上角位置的顺序,也应该变成斜 45 45 45度方向

同时双指针的写法由于每次长度只扩展 1 1 1,因此可以只使用一维前缀和,当然使用二维前缀和代码会更简洁和容易理解。
下方代码写的是一维前缀和。

#include<bits/stdc++.h>
using namespace std;

int n,m;
char field[1007][1007];

struct Node{
    int T,Z,O,J;
}sum[2][1007][1007];
//sum[0]是横向的前缀和
//sum[1]是竖向的前缀和

int main(){
    memset(sum,0,sizeof(sum));
    cin>>n>>m;
    for(int i=0;i<n;i++) cin>>field[i];
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            if(field[i][j]=='T') sum[0][i+1][j+1].T=sum[1][i+1][j+1].T=1;
            else if(field[i][j]=='Z') sum[0][i+1][j+1].Z=sum[1][i+1][j+1].Z=1;
            else if(field[i][j]=='O') sum[0][i+1][j+1].O=sum[1][i+1][j+1].O=1;
            else if(field[i][j]=='J') sum[0][i+1][j+1].J=sum[1][i+1][j+1].J=1;
        }
    }
    //做横向前缀和
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            sum[0][i][j].T+=sum[0][i][j-1].T;
            sum[0][i][j].Z+=sum[0][i][j-1].Z;
            sum[0][i][j].O+=sum[0][i][j-1].O;
            sum[0][i][j].J+=sum[0][i][j-1].J;
        }
    }
    //做竖向前缀和
    for(int j=1;j<=m;j++){
        for(int i=1;i<=n;i++){
            sum[1][i][j].T+=sum[1][i-1][j].T;
            sum[1][i][j].Z+=sum[1][i-1][j].Z;
            sum[1][i][j].O+=sum[1][i-1][j].O;
            sum[1][i][j].J+=sum[1][i-1][j].J;
        }
    }
    int ans=0;
    //以field[nowi][nowj]作为我们正方形区域的左上角,共n^2种情况,借助双指针思想,整体复杂度仍为O(n^2)
    //先计算整个矩形右上角一侧的点作为正方形区域的左上角的情况
    for(int j=1;j<=m;j++){
        int nowi=1,nowj=j;//field[nowi][nowj]作为正方形区域的左上角
        int to_i=nowi,to_j=nowj;//field[to_i][to_j]作为正方形区域的右下角
        int T=sum[0][nowi][nowj].T-sum[0][nowi][nowj-1].T;
        int Z=sum[0][nowi][nowj].Z-sum[0][nowi][nowj-1].Z;
        int O=sum[0][nowi][nowj].O-sum[0][nowi][nowj-1].O;
        int J=sum[0][nowi][nowj].J-sum[0][nowi][nowj-1].J;
        //T,Z,O,J统计当前正方形区域中对应字符的个数
        while(nowi<=n&&nowj<=m){         
            while(1){
                if(T&&Z&&O&&J) break;//当前边长足够长,包含了四种字符
                if(to_i==n||to_j==m) break;//右下角已经无法再往右下方向拓展
                //右侧增加一列,对应下标field[nowi][to_j+1]到field[to_i+1][to_j+1]
                T+=sum[1][to_i+1][to_j+1].T-sum[1][nowi-1][to_j+1].T;
                Z+=sum[1][to_i+1][to_j+1].Z-sum[1][nowi-1][to_j+1].Z;
                O+=sum[1][to_i+1][to_j+1].O-sum[1][nowi-1][to_j+1].O;
                J+=sum[1][to_i+1][to_j+1].J-sum[1][nowi-1][to_j+1].J;
                //底部增加一行,对应下标field[to_i+1][nowj]到field[to_i+1][to_j]//这里没有+1,右下角那个点别算重复了
                T+=sum[0][to_i+1][to_j].T-sum[0][to_i+1][nowj-1].T;
                Z+=sum[0][to_i+1][to_j].Z-sum[0][to_i+1][nowj-1].Z;
                O+=sum[0][to_i+1][to_j].O-sum[0][to_i+1][nowj-1].O;
                J+=sum[0][to_i+1][to_j].J-sum[0][to_i+1][nowj-1].J;
                to_i++;
                to_j++;
                //右下角往右下尝试拓展长度1
            }
            if(T&&Z&&O&&J) ans+=min(n-to_i+1,m-to_j+1);//比当前右下角更右下的位置都满足答案
            //左上角尝试往右下挪动距离1的位置
            //上侧减少一行,对应下标field[nowi][nowj]到field[nowi][to_j]
            T-=sum[0][nowi][to_j].T-sum[0][nowi][nowj-1].T;
            Z-=sum[0][nowi][to_j].Z-sum[0][nowi][nowj-1].Z;
            O-=sum[0][nowi][to_j].O-sum[0][nowi][nowj-1].O;
            J-=sum[0][nowi][to_j].J-sum[0][nowi][nowj-1].J;
            //左侧减少一列,对应下标field[i+1][nowj]到field[to_i][nowj]   //这里也一样别把左上角的点多算了一次
            T-=sum[1][to_i][nowj].T-sum[1][nowi][nowj].T;
            Z-=sum[1][to_i][nowj].Z-sum[1][nowi][nowj].Z;
            O-=sum[1][to_i][nowj].O-sum[1][nowi][nowj].O;
            J-=sum[1][to_i][nowj].J-sum[1][nowi][nowj].J;
            nowi++;
            nowj++;
        }
    }
    //再计算整个矩形左下角一侧的点作为正方形区域的左上角的情况
    for(int i=2;i<=n;i++){
        int nowi=i,nowj=1;
        int to_i=nowi,to_j=nowj;//field[to_i][to_j]作为正方形区域的右下角
        int T=sum[0][nowi][nowj].T-sum[0][nowi][nowj-1].T;
        int Z=sum[0][nowi][nowj].Z-sum[0][nowi][nowj-1].Z;
        int O=sum[0][nowi][nowj].O-sum[0][nowi][nowj-1].O;
        int J=sum[0][nowi][nowj].J-sum[0][nowi][nowj-1].J;
        //T,Z,O,J统计当前正方形区域中对应字符的个数
        while(nowi<=n&&nowj<=m){         
            while(1){
                if(T&&Z&&O&&J) break;//当前边长足够长,包含了四种字符
                if(to_i==n||to_j==m) break;//右下角已经无法再往右下方向拓展
                //右侧增加一列,对应下标field[nowi][to_j+1]到field[to_i+1][to_j+1]
                T+=sum[1][to_i+1][to_j+1].T-sum[1][nowi-1][to_j+1].T;
                Z+=sum[1][to_i+1][to_j+1].Z-sum[1][nowi-1][to_j+1].Z;
                O+=sum[1][to_i+1][to_j+1].O-sum[1][nowi-1][to_j+1].O;
                J+=sum[1][to_i+1][to_j+1].J-sum[1][nowi-1][to_j+1].J;
                //底部增加一行,对应下标field[to_i+1][nowj]到field[to_i+1][to_j]//这里没有+1,右下角那个点别算重复了
                T+=sum[0][to_i+1][to_j].T-sum[0][to_i+1][nowj-1].T;
                Z+=sum[0][to_i+1][to_j].Z-sum[0][to_i+1][nowj-1].Z;
                O+=sum[0][to_i+1][to_j].O-sum[0][to_i+1][nowj-1].O;
                J+=sum[0][to_i+1][to_j].J-sum[0][to_i+1][nowj-1].J;
                to_i++;
                to_j++;
                //右下角往右下尝试拓展长度1
            }
            if(T&&Z&&O&&J) ans+=min(n-to_i+1,m-to_j+1);//比当前右下角更右下的位置都满足答案
            //左上角尝试往右下挪动距离1的位置
            //上侧减少一行,对应下标field[nowi][j]到field[nowi][to_j]
            T-=sum[0][nowi][to_j].T-sum[0][nowi][nowj-1].T;
            Z-=sum[0][nowi][to_j].Z-sum[0][nowi][nowj-1].Z;
            O-=sum[0][nowi][to_j].O-sum[0][nowi][nowj-1].O;
            J-=sum[0][nowi][to_j].J-sum[0][nowi][nowj-1].J;
            //左侧减少一列,对应下标field[nowi+1][nowj]到field[to_i][nowj]   //这里也一样别把左上角的点多算了一次
            T-=sum[1][to_i][nowj].T-sum[1][nowi][nowj].T;
            Z-=sum[1][to_i][nowj].Z-sum[1][nowi][nowj].Z;
            O-=sum[1][to_i][nowj].O-sum[1][nowi][nowj].O;
            J-=sum[1][to_i][nowj].J-sum[1][nowi][nowj].J;
            nowi++;
            nowj++;
        }
    }
    cout<<ans<<endl;
}

H题

佳加简减(tzcoder8397)

思路

看起来吓人,其实很简单
非严格上升子序列的长度 + + +非严格下降子序列的长度尽可能大
非严格上升子序列的最大长度是 n n n
非严格下降子序列的最大长度是 n n n
能不能两个同时取 n n n
可以,整个数列里的数都相等就可以。

那么这道题就变成了把整个数组里的所有数变成相同的同一个数,最少的操作次数

这就是一个经典的中位数的题目了

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+7;

int num[maxn];

int main(){
	int n;
	scanf("%d",&n); 
	for(int i=0;i<n;i++) scanf("%d",&num[i]);
	sort(num,num+n);
	long long ans=0;
	int base=num[n/2];
	for(int i=0;i<n;i++) ans+=abs(num[i]-base);
	printf("%lld\n",ans); 
}

I题

Auuuu的水题(tzcoder8398)

思路

二维平面上的多源bfs
直接把所有初始为水源的点压到队列里,统计每个非水源点周围水源点的个数。
每次从队列里弹出一个水源位置,更新周围非水源点记录的水源点个数信息,一旦有 2 2 2个以上水源就把这个位置修改为水源,同时压入队列中等待更新周边位置。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=507;

int n,m;
char field[maxn][maxn];
int cnt[maxn][maxn];//cnt[i][j]代表(i,j)这个位置周围有几个点是水源
struct Node{
    int x,y;
};
queue<Node>Q;

int dir[4][2]={//对应朝着四个方向走
    1,0,
    -1,0,
    0,1,
    0,-1
};

int main(){
    cin>>n>>m;
    for(int i=0;i<n;i++) cin>>field[i];
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            if(field[i][j]=='*') Q.push({i,j});
        }
    }
    while(Q.size()){
        Node now=Q.front();
        Q.pop();
        for(int i=0;i<4;i++){
            int nextx=now.x+dir[i][0];
            int nexty=now.y+dir[i][1];
            if(nextx>=0&&nextx<n&&nexty>=0&&nexty<m){//保证在地图内
                if(field[nextx][nexty]=='.'){
                    cnt[nextx][nexty]++;//周围水源点个数+1
                    if(cnt[nextx][nexty]==2){//超过两个点
                        Q.push({nextx,nexty});
                        field[nextx][nexty]='*';//当前位置变为水源点
                    }
                }
            }
        }
    }
    for(int i=0;i<n;i++) cout<<field[i]<<endl;
}

J题

Auuuu的粥游账号(tzcoder8399)

思路

dfs暴力枚举所有情况的经典题,同时兼备字典序
必须学会
后期骗分必备写法

#include<bits/stdc++.h>
using namespace std;

int n;
bool flag[27];//记录每个数有没有被使用过 
struct Node{
	int num[12];
};
vector<Node>out;
Node temp;

void dfs(int deep,int x){//x代表前面用过的数最大是多少 
	if(deep==12){
		out.push_back(temp);
		return;
	}
	//deep代表当前暴力枚举第deep个数放什么
	for(int j=x+1;j<=n;j++){
		//注意从小到大枚举,这样就可以保证字典序
		if(flag[j]) continue;//被用过了就跳过
		flag[j]=1;//标记为被使用过
		temp.num[deep]=j;
		dfs(deep+1,j);
		flag[j]=0;//记得还原 
	} 
}

int main(){
	cin>>n;
	dfs(0,0);
	cout<<out.size()<<endl;
	for(int i=0;i<out.size();i++){
		for(int j=0;j<12;j++){
			if(j) cout<<' ';
			cout<<out[i].num[j];
		}
		cout<<endl;
	}
}

K题

上课不要讲话 (tzcoder8400)

思路

可以把第一个人能坐的所有位置的周围位置都打上标记
然后监测第二个人能不能坐在打上标记的位置上即可
注意不要忘记考虑两个人初始位置也是一种选择

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=107;

int n,m,a,b;
bool flag[maxn][maxn];
//flag[i][j]代表第i行第j列这个位置能不能与Richadal相邻

int main(){
    cin>>n>>m>>a>>b;
    flag[0][1]=flag[1][0]=flag[1][1]=1;//别忘了初始位置
    while(a--){
        int temp;
        cin>>temp;
        //temp对应在第(temp-1)/m行,第(temp-1)%m列
        int nowr=(temp-1)/m,nowc=(temp-1)%m;
        for(int i=-1;i<=1;i++){
            for(int j=-1;j<=1;j++){//枚举周围8个格子
                if(i==0&&j==0) continue;

                if(nowr+i>=0&&nowr+i<n&&nowc+j>=0&&nowc+j<m) flag[nowr+i][nowc+j]=1;
            }
        }
    }
    while(b--){
        int temp;
        cin>>temp;
        int nowr=(temp-1)/m,nowc=(temp-1)%m;
        if(flag[nowr][nowc]){
            cout<<"Yes"<<endl;
            return 0;
        }
    }
    //这里也不要忘记初始位置
    if(flag[n-1][m-1]){
        cout<<"Yes"<<endl;
        return 0;
    }
    cout<<"No"<<endl;
}

L题

Richadal和他的采矿车 (tzcoder8401)

思路

模拟
多关键字结构体排序
有一个坑点是时间可能会爆int,当目标矿总量 1 0 9 10^9 109,而每次只能运 1 1 1矿石时,所需搬运次数是 1 0 9 10^9 109,再乘以每次来回所需时间会超过int。
要学会分析数据范围,不要简单直接#define int long long
long long的计算常数大于int ,并且带来更多内存消耗,以及容易没有分析数据范围的习惯,爆long long的情况可能会反应不过来。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e3+7;

int n;
int x[maxn],y[maxn],z[maxn],s[maxn];
struct Node{
    long long time;//完成采矿需要的时间
    int tag;//编号
    int cnt;//需要进行的往返次数
}node[maxn];

bool cmp1(Node a,Node b){//采矿快的在前
    if(a.time==b.time) return a.tag<b.tag;
    return a.time<b.time;
}

bool cmp2(Node a,Node b){//轮数少的在前
    if(a.cnt==b.cnt) return a.tag<b.tag;
    return a.cnt<b.cnt;
}

int main(){
    int t;
    cin>>t;
    while(t--){
        cin>>n;
        for(int i=0;i<n/2;i++) cin>>x[i]>>y[i]>>z[i]>>s[i];
        for(int i=n/2;i<n;i++) cin>>x[i]>>y[i]>>s[i];
        int target,k;
        cin>>target>>k;
        for(int i=0;i<n/2;i++){
            node[i].tag=i+1;
            node[i].cnt=(target-1)/s[i]+1;
            node[i].time=node[i].cnt*(long long)(x[i]+y[i]+z[i]);
        }
        for(int i=n/2;i<n;i++){
            node[i].tag=i+1;
            node[i].cnt=(target-1)/s[i]+1;
            node[i].time=node[i].cnt*(long long)(x[i]+y[i]);
        }
        int tempcnt=node[k-1].cnt;//排序后编号k的矿车位置可能改变
        //提前记录下第二个输出
        sort(node,node+n,cmp1);
        cout<<node[k-1].tag<<' '<<node[k-1].cnt<<endl;
        cout<<k<<' '<<tempcnt<<endl;
        sort(node,node+n,cmp2);
        cout<<node[k-1].tag<<' '<<node[k-1].cnt<<endl;
    }
}

M题

yuyu与baby斗地主 (tzcoder8402)

思路

模拟
分类讨论
这里用一个change函数,把牌面转换用具体的数值来表示,数值越大牌面越大

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e3+7;

int n,m;
int cnt1[17],cnt2[17];//统计两个人每种牌有几张

int change(string s){
    if(s.size()>1) return 10;
    if(s[0]>='3'&&s[0]<='9') return s[0]-'0';
    if(s[0]=='J') return 11;
    if(s[0]=='Q') return 12;
    if(s[0]=='K') return 13;
    if(s[0]=='A') return 14;
    if(s[0]=='2') return 15;
    return 16;//剩下的只有大小王
}

int main(){
    cin>>n>>m;
    while(n--){
        string s;
        cin>>s;
        cnt1[change(s)]++;
    }
    while(m--){
        string s;
        cin>>s;
        cnt2[change(s)]++;
    }
    vector<int>S1,S2;//保存两个人的炸弹
    if(cnt1[16]==2) S1.push_back(16);
    for(int i=15;i>=3;i--)
        if(cnt1[i]==4) S1.push_back(i);
    if(cnt2[16]==2) S2.push_back(16);
    for(int i=15;i>=3;i--)
        if(cnt2[i]==4) S2.push_back(i);
    if(S1.size()>S2.size()) cout<<"yuyu"<<endl;
    else if(S1.size()<S2.size()) cout<<"baby"<<endl;
    else{
        if(S1.size()==0){//两个热都没有炸弹,平局
            cout<<"draw"<<endl;
            return 0;
        }
        int flag=0;//代表yuyu比拼炸弹赢baby的次数
        for(int i=0;i<S1.size();i++){
            flag+=S1[i]>S2[i]?1:-1;
        }
        if(flag>0) cout<<"yuyu"<<endl;
        else if(flag<0) cout<<"baby"<<endl;
        else{
            if(S1[0]>S2[0]) cout<<"yuyu"<<endl;
            else cout<<"baby"<<endl;
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考
“中国东信杯”广西大学第四届程序设计竞赛(同步赛)题“中国东信杯”广西大学第四届程序设计竞赛(同步赛)题“中国东信杯”广西大学第四届程序设计竞赛(同步赛)题“中国东信杯”广西大学第四届程序设计竞赛(同步赛)题“中国东信杯”广西大学第四届程序设计竞赛(同步赛)题“中国东信杯”广西大学第四届程序设计竞赛(同步赛)题“中国东信杯”广西大学第四届程序设计竞赛(同步赛)题“中国东信杯”广西大学第四届程序设计竞赛(同步赛)题“中国东信杯”广西大学第四届程序设计竞赛(同步赛)题“中国东信杯”广西大学第四届程序设计竞赛(同步赛)题“中国东信杯”广西大学第四届程序设计竞赛(同步赛)题“中国东信杯”广西大学第四届程序设计竞赛(同步赛)题“中国东信杯”广西大学第四届程序设计竞赛(同步赛)题“中国东信杯”广西大学第四届程序设计竞赛(同步赛)题“中国东信杯”广西大学第四届程序设计竞赛(同步赛)题“中国东信杯”广西大学第四届程序设计竞赛(同步赛)题“中国东信杯”广西大学第四届程序设计竞赛(同步赛)题“中国东信杯”广西大学第四届程序设计竞赛(同步赛)题“中国东信杯”广西大学第四届

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值