深入浅出程序设计竞赛(洛谷基础篇) 第八章 模拟与高精度

电子版教材链接
我通过百度网盘分享的文件:深入浅出程序设计…pdf
链接:https://pan.baidu.com/s/1kmF8wZLnK3Zci7s1ffjRzw
提取码:Ra3Q
复制这段内容打开「百度网盘APP即可获取」

本章大多都是模拟题,没有用到很高深的算法,主要都是用朴素的思维来解决问题,仅仅本文的题目不足以掌握模拟的精髓,希望读者可以多练一些模拟题来升华自己的思维
例8-1 乒乓球
// 法一: 简洁实现
#include
#include
using namespace std;

char str[100010];
int cnt=0;

  
void show(int n){
	int a=0,b=0;
	for(int i=0;i<cnt;i++){
		if(str[i]=='W') a++;
		if(str[i]=='L') b++;
		if((a>=n||b>=n)&&abs(a-b)>=2){
		cout<<a<<":"<<b<<endl;
		a=b=0;
		}
	}
	//新的一轮刚开始,或上一局没有打完
	cout<<a<<":"<<b<<endl;
}

  

int main(){
	char ch;
	while(cin>>ch&&ch!='E'){
		if(ch=='W'||ch=='L'){
			str[cnt++]=ch;
		}
	}
	
	show(11);
	cout<<endl;
	show(21);
	return 0;
}

// 法二:完整实现
#include 
using namespace std;
char n;
int l,m;
int a[62503];
int main()
{
	for(int i = 0;cin >> n && n != 'E';i++)
	{
		if(n == 'W') a[i] = 1;
		if(n == 'L') a[i] = 2;
	}
	// 11分制
	for(int i = 0;1;i++)
	{
		if(a[i] == 1) l++;
		if(a[i] == 2) m++;
		if(a[i] == 0)
		{
			cout << l << ":" << m << endl << endl;
			break;
		}
		if(l - m >= 2 || m - l >= 2)
		{
			if(l >= 11 || m >= 11)
			{
				cout << l << ":" << m << endl;
				l = 0;
				m = 0;
			}
		}
	}
	
	l = 0;
	m = 0;
	
	// 21分制
	for(int i = 0;1;i++)
	{
		if(a[i] == 1) l++;
		if(a[i] == 2) m++;
		if(a[i] == 0)
		{
			cout << l << ":" << m << endl;  //把未打完的残局也输出出去
			break;
		}
		if(l - m >= 2 || m - l >= 2)
		{
			if(l >= 21 || m >= 21)
			{
				cout << l << ":" << m << endl;
				l = 0;
				m = 0;
			}
		}
	}
	return 0;
}
例8-2 扫雷游戏
// 法一:开桶
#include 
using namespace std;
int n,m;
char arr[105][105];

const int dx[]  = {1,1,1,0,0,-1,-1,-1};
const int dy[] = {-1,0,1,-1,1,-1,0,1};

// 这边定义了该点周围八个方向的点的偏移量

int main()
{
    cin >> n >> m;
    for(int i = 1;i<=n;i++)
    {
        for(int j = 1;j<=m;j++)
        {
            cin >> arr[i][j];
        }
    }
    for(int i = 1;i<=n;i++)
    {
        for(int j = 1;j<=m;j++)
        {
            if(arr[i][j] != '*')
            {
                int cnt = 0;
                for(int k = 0;k<8;k++)
                {
                    if(arr[i+dx[k]][j+dy[k]] == '*') cnt++;
                }
                cout << cnt;
            }
            else cout << '*';
        }
        cout << endl;
    }
    return 0;
}

// 法二:dfs
#include 
using namespace std;
int n,m;
char arr[105][105];

void dfs(int x,int y){//需要返回空类型,定义void函数 
    if(arr[x][y]=='0'){
        for(int dx=-1;dx<=1;dx++){
            for(int dy=-1;dy<=1;dy++){
                int nx=dx+x,ny=dy+y;
                if(0<=nx&&nx<n&&0<=ny&&ny<m&&arr[nx][ny]=='*')//八连通循环 ,且必须不越界
                arr[x][y]+=1;//如果是雷的话将那一个点加1 
            }
        }
    }
    return;//将那个点加完之后就返回继续找下一个点 
} 
int main()
{
    string s;
    cin >> n >> m;
    for(int i = 0; i < n; i++)
    {
        cin >> s;
        for(int j = 0; j < m; j++)
        {
            arr[i][j] = s[j];
            if(arr[i][j] == '?')
	        {
	            arr[i][j] = '0';
	        }
        }
    }
    for(int i = 0;i<n;i++)
    {
        for(int j = 0;j<m;j++)
            dfs(i,j);
    }
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++)
            cout<<arr[i][j];//按序输出每一个点 
        cout << endl;
    }
    return 0;
}
例8-3 玩具谜题
#include 
using namespace std;

struct nihao{
	bool dict;
	string name;
}arr[100005];

int main()
{
	bool s;
	int len,cnt = 0; // cnt表示的是最后我们要找的人的索引
	cin >> n >> m;
	for(int i = 0;i<n;i++)
	{
		cin >> arr[i].dict >> arr[i].name;
	} 
	for(int i = 0;i<m;i++)
	{
		cin >> s >> len; // 0表示左数,1表示右数 ;其中朝内向左等价于朝外向右,即00和11是一致的,我们主要考虑朝内向右为正 01
		if(arr[cnt].dict == 0 && s== 0 || arr[cnt].dict == 1 && s == 1)
		{
			cnt -= len;
		}
		else
			cnt += len;
		if(cnt >=n)
			cnt-=n;
		else if(cnt<0)
			cnt+=n;
	}
	cout << arr[cnt].name << endl;
	return 0;
}
例8-4 高精度加法

直接使用取模的方式从低位往高位进行进位,这里是高精度加上高精度

#include 
using namespace std;
#define maxn 520
int a[maxn],b[maxn],c[maxn];

int main()
{
	string A,B;
	cin >> A >> B;
	int len = max(A.length(),b.length());
	for(int i = A.length()-1,j = 1;i>=0;i--,j++)
		a[j] = A[i] - '0';
	for(int i = B.length()-1,j = 1;i>=0;i--,j++)
		b[j] = B[i] - '0';
	for(int i = 1;i<=len;i++)
	{
		c[i] += a[i] + b[i];
		c[i+1] = c[i] / 10;
		c[i] %= 10;
	}
	if(c[len+1]) // 最后进位可能会导致位数增加
		len++;
	for(int i = len;i>=1;i--)
		cout << c[i];
	return 0;
}
拓展 高精度减法

注意: 这里的A一定要大于B!

Q:为什么要考虑前导0
A:
1000 - 1 = 0999 会输出0999而不是999

#include 
using namespace std;

#define maxn 520

int a[maxn], b[maxn], c[maxn];

int main()
{
    string A, B;
    cin >> A >> B;

    int lenA = A.length();
    int lenB = B.length();
    int len = max(lenA, lenB);

    // 倒序存入数组 a[], b[]
    for (int i = lenA - 1, j = 1; i >= 0; i--, j++)
        a[j] = A[i] - '0';
    for (int i = lenB - 1, j = 1; i >= 0; i--, j++)
        b[j] = B[i] - '0';

    // 高精度减法:c = a - b(要求 a ≥ b)
    for (int i = 1; i <= len; i++) {
        c[i] = a[i] - b[i];
        if (c[i] < 0) {
            c[i] += 10;
            a[i + 1]--; // 向高位借 1
        }
    }

    // 去除前导零
    while (len > 1 && c[len] == 0) len--;  // 1000-1 = 0999,要把前导0减去

    // 输出结果
    for (int i = len; i >= 1; i--)
        cout << c[i];
    cout << endl;

    return 0;
}

例8-5 高精度乘法

这里是高精度乘高精度的板子题

先计算贡献,最后一次性处理进位,一次性取模进位的复杂度比多次取模进位的复杂度要低得多

Q:为什么要考虑前导0的情况

A:
100 × 100 = 10000
最大位数 = 3 + 3 = 6
实际结果 = 5 位 → c[6] = 0,导致我们输出的不是10000而是010000

#include 
using namespace std;
#define maxn 5010
int a[maxn],b[maxn],c[maxn];

int main()
{
	string A,B;
	cin >> A >> B;
	int lena = A.length(),lenb = B.length();
	for(int i = lena-1;i>=0;i--) a[lena-i] = A[i] - '0';
	for(int i = lenb-1;i>=0;i--) b[lenb-i] = B[i] - '0';
	for(int i = 1;i<=lena;i++)
		for(int j = 1;j<=lenb;j++)
 			c[i+j-1] += a[i] * b[j]; //计算贡献 a[i]*b[j]的进位贡献都在[i+j-1]上
	int len  = lena + lenb; // 乘积的位数不超过两数的位数之和
	for(int i = 1;i<=len;i++){
		c[i+1] += c[i] / 10; // 处理进位
		c[i] %=10;
	}
	while (len > 1 && c[len] == 0) len--; // 去除前导零
	for(int i = len;i>=1;i--)
		cout << c[i];
	return 0;
}
拓展 高精度乘单精度
#include 
using namespace std;
#define maxn 5010
int a[maxn], c[maxn]; // a 是大整数,c 是结果
int b; // 单精度整数

int main()
{
	string A;
	cin >> A >> b; // 读入大整数和单精度整数

	int lena = A.length();
	for(int i = lena - 1; i >= 0; i--) 
		a[lena - i] = A[i] - '0'; // 逆序存储,大端 -> 小端

	int len = lena;
	for(int i = 1; i <= lena; i++) {
		c[i] += a[i] * b; // 乘以单精度数
	}

	// 处理进位
	for(int i = 1; i <= len + 10; i++) {
		c[i + 1] += c[i] / 10;
		c[i] %= 10;
	}
	while(len > 1 && c[len] == 0) len++; // 预估最大长度后再定位真实长度
	while(len > 1 && c[len] == 0) len--; // 去除前导 0

	for(int i = len; i >= 1; i--) 
		cout << c[i];
	cout << endl;
	return 0;
}
拓展 高精度除法

tips: 高精度除法是从高位往低位处理,所以不需要倒置,这里实现的是高精度大数除以小数的版本,更严谨的高精度版本会在后续的vector容器部分进行展示。

设被除数为字符串 A,除数为整数 b,求商 C = A / b,余数 r = A % b。处理方法如下:

  1. 从高位到低位 依次处理 A 的每一位字符。

  2. 设置当前的“部分被除数”为 r * 10 + A[i]

  3. 当前位的商为 (r * 10 + A[i]) / b,更新余数 r = (r * 10 + A[i]) % b

  4. 依次填入商的每一位,最后去除前导零即可。

这种做法等价于手动进行“竖式除法”中从左向右的计算

#include 
using namespace std;
#define maxn 5010
int a[maxn], c[maxn]; // a 存储被除数,c 存储结果商

int main() {
    string A;
    int b; // 除数是一个普通整数
    cin >> A >> b;

    int lena = A.length();

    // 以高位在前方式读入数组(a[1] 是最高位)
    for(int i = 0; i < lena; i++) {
        a[i+1] = A[i] - '0';
    }

    int r = 0; // 当前余数
    for(int i = 1; i <= lena; i++) {
        int current = r * 10 + a[i]; // 当前部分被除数
        c[i] = current / b; // 当前位的商
        r = current % b;     // 更新余数
    }

    // 去除前导零
    int s = 1;
    while(s < lena && c[s] == 0) s++;

    for(int i = s; i <= lena; i++)
        cout << c[i];
    if (s > lena) cout << 0; // 特判商为0的情况
	cout << r << endl; // 输出余数
    return 0;
}

例8-6 阶乘之和
#include 
using namespace std;

#define maxn 100
struct Bigint{
	int len,a[maxn]; // 使用len记录位数(长度),a为记录每个数位的数组
	Bigint(int x = 0) // 通过初始化是的这个大整数能够表示整型x,默认为0
	{
		memset(a,0,sizeof(a));
		for(len = 1;x;len++)
			a[len] = x%10 , x/=10;
		len--;
	}
	int &operator[](int i){
		return a[i]; //重载[],可以直接使用x[i]表示x.a[i],这一步可以省去
	}
	void flatten(int L) // 因为把不是一位数的数都处理为一位数,所以函数名叫做展平
  	{
		// 一口气处理1到L范围内的进位并且重置长度,但需要保证L不小于有效长度
		len = L;
		for(int i = 1;i<=len;i++)
			a[i+1] += a[i]/10,a[i] %=10	;
		while (len > 1 && a[len] == 0) len--; // 去除前导零
	}
	void print(){ // 输出
		for(int i= len;i>=1;i--)
			printf("%d",a[i]);
	}
};

// 重载加法和乘法运算符
Bigint operator+(Bigint a,Bigint b){  // 表示两个Bigint类相加,返回一个Bigint类
	Bigint c;
	int len = max(a.len,b.len);
	for(int i = 1;i<=len;i++)
		c[i] += a[i]+b[i]; // 计算贡献
	c.flatten(len+1); // 答案不超过len+1位,使用len+1做一遍展平处理进位(多的位数会被当前导0被处理掉)
	return c;
}

// 这里由于阶乘每次乘的数都是不超过1e9的int类型,所以我们只需要使用高精度*int类型的乘法就够了
Bigint operator*(Bigint a,int b){ // 表示Bigint乘整型变量,返回一个Bigint类
	Bigint c;
	int len = a.len;
	for(int i = 1 ;i<=len;i++)
		c[i] = a[i] * b; // 计算贡献
	c.flatten(len+11); // int类型最长10位,这样做可以一遍展平处理进位(多的位数会被当前导0被处理掉)
	return c;
}

int main()
{
	Bigint ans(0),fac(1) ; // 调用构造函数进行初始化
	int n;
	cin >> n;
	for(int i = 1;i<=n;i++){
		fac = fac*i;
		ans = ans+fac;
	}
	ans.print(); // 输出答案
}


习题8-1 魔法少女小Scarlet
#include
#include
using namespace std;
int square[505][505],temp[505][505];

// 先简单的使用r = 1为一半数据的情况进行模拟分析,找出规律后使用规律构建函数进行解决
void spin(int x,int y,int r)//顺时针旋转90°
{
	for(int i=x-r;i<=x+r;i++)
	{
		for(int k=y-r;k<=y+r;k++)
			temp[i][k]=square[i][k];  // 新开辟一个数组存储原来的信息,否则后续的更新中可能会存在覆盖 
            // 比如你第一个旋转是 a[x-1][y-1] = a[x+1][y-1]  但是第三个旋转是 a[x-1][y+1] = a[x-1][y-1] 如果不使用新数组这两行代码就相当于 a[x-1][y+1] = a[x+1][y-1] 但是根据分析,这个是不成立的,所以每次更新我们需要使用一个新数组记录原数组的信息,所以上面的旋转过程应该是 a[x-1][y-1] = temp[x+1][y-1],a[x-1][y+1] = temp[x-1][y-1]; 
	}
	int x1=x+r,y1=y-r;
	for(int i=x-r;i<=x+r;i++)
	{
		for(int k=y-r;k<=y+r;k++)
		{
			square[i][k]=temp[x1][y1];
			x1--;
		}
		x1=x+r,y1++;
	}
}

void spin_(int x,int y,int r)//逆时针旋转90°
{
	for(int i=x-r;i<=x+r;i++)
	{
		for(int k=y-r;k<=y+r;k++)
			temp[i][k]=square[i][k];
	}
	int x1=x-r,y1=y+r;
	for(int i=x-r;i<=x+r;i++)
	{
		for(int k=y-r;k<=y+r;k++)
		{
			square[i][k]=temp[x1][y1];
			x1++;
		}
		y1--,x1=x-r;
	}
}

int main()
{
	int n,m,t=0;
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		for(int k=1;k<=n;k++)
			square[i][k]=++t;
	}//首先给矩阵赋值
	int x,y,r,z;
	for(int i=0;i<m;i++)
	{
		cin>>x>>y>>r>>z;
		if(z==0)
			spin(x,y,r);
		else if(z==1)
			spin_(x,y,r);
	}
	for(int i=1;i<=n;i++)
	{
		for(int k=1;k<=n;k++)
			cout<<square[i][k]<<" ";
		cout<<endl;
	}
}
习题 8-2 生活大爆炸版剪刀石头布
#include 
using namespace std;
int N,N_A,N_B;
int arr[205],brr[205],a[205],b[205],ans = 0,bns = 0;

void nihao(int a[],int b[],int & ans,int &bns)
{
    for(int i = 0;i<N;i++)
    {
        if(a[i] == 0 && b[i] == 0 || a[i] == 1 && b[i] == 1 || a[i] == 2 && b[i] == 2 || a[i] == 3 && b[i] == 3 || a[i] == 4 && b[i] == 4) ans +=0,bns+=0;
        if(a[i] == 0 && b[i] == 1) bns++;
        if(a[i] == 0 && b[i] == 2) ans++;
        if(a[i] == 0 && b[i] == 3) ans++;
        if(a[i] == 0 && b[i] == 4) bns++;
        if(a[i] == 1 && b[i] == 0) ans++;
        if(a[i] == 1 && b[i] == 2) bns++;
        if(a[i] == 1 && b[i] == 3) ans++;
        if(a[i] == 1 && b[i] == 4) bns++;
        if(a[i] == 2 && b[i] == 0) bns++;
        if(a[i] == 2 && b[i] == 1) ans++;
        if(a[i] == 2 && b[i] == 3) bns++;
        if(a[i] == 2 && b[i] == 4) ans++;
        if(a[i] == 3 && b[i] == 0) bns++;
        if(a[i] == 3 && b[i] == 1) bns++;
        if(a[i] == 3 && b[i] == 2) ans++;
        if(a[i] == 3 && b[i] == 4) ans++;
        if(a[i] == 4 && b[i] == 0) ans++;
        if(a[i] == 4 && b[i] == 1) ans++;
        if(a[i] == 4 && b[i] == 2) bns++;
        if(a[i] == 4 && b[i] == 3) bns++;
    }
}


int main()
{
    cin >> N >> N_A >> N_B;
   
    for(int i = 0;i<N_A;i++)
    {
        cin >> arr[i];
    }
    for(int i = 0;i<N_B;i++)
    {
        cin >> brr[i];
    }
    for (int i = 0; i < N; i++) {
        a[i] = arr[i % N_A]; 
    }
    for (int i = 0; i < N; i++) {
        b[i] = brr[i % N_B]; 
    }
    nihao(a,b,ans,bns);
    cout << ans << " " << bns;
    return 0;
}
习题8-3 两只塔姆沃斯牛

// 法一: dfs暴力搜索
#include
using namespace std;
const int dx[4]={-1,0,1,0};//上、右、下、左四个方向 
const int dy[4]={0,1,0,-1};
int cx,cy,ct;//记录奶牛 
int fx,fy,ft;//记录john 
bool f[21][21],v[21][21][4][21][21][4];//f表示地图是否能走,v表示这种情况是否有出现过:农夫在 (fx, fy) 且朝向 ft,奶牛在 (cx, cy) 且朝向 ct。
void dfs(int k)
{
	if(cx==fx && cy==fy)//如果抓到了牛 
	{
		printf("%d\n",k);//输出 
		exit(0);
	}
	if(v[fx][fy][ft][cx][cy][ct]==false)//如果这种情况出现过 
	{
		printf("0\n");
		exit(0);
	}
	v[fx][fy][ft][cx][cy][ct]=false;//把这种情况设置为出现过 
	//找奶牛的方向 
	if(f[cx+dx[ct]][cy+dy[ct]]==false)//如果这个方向不能走 
	{
		ct++;if(ct==4) ct=0;//改变方向 
	}
	else cx=cx+dx[ct],cy=cy+dy[ct];//如果可以走就走 
	//找john的方向 
	if(f[fx+dx[ft]][fy+dy[ft]]==false) 
	{
		ft++;if(ft==4) ft=0;
	}
	else fx=fx+dx[ft],fy=fy+dy[ft];
	dfs(k+1);//往下搜索 
}
int main()
{
	int i,j;char st[21];
	memset(f,false,sizeof(f));//为了方便判断边界,一开始就当做不能走吧 
	for(i=1;i<=10;i++)
	{
		scanf("%s",st+1);//输入 
		for(j=1;j<=10;j++)
		{
			if(st[j]=='.') f[i][j]=true;//如果能走 
			if(st[j]=='F') fx=i,fy=j,f[i][j]=true;//如果是john 
			if(st[j]=='C') cx=i,cy=j,f[i][j]=true;//如果是牛 
		}
	}
	ct=ft=0;//一开始是向北 
	memset(v,true,sizeof(v));//各种情况都是可以的 
	dfs(0);//开始搜索 
	return 0;
}

// 法二: 模拟骗分
#include  
using namespace std ;

//基本初始:
char mp[11][11] ; //创建地图
int xa , ya , xb , yb ;	//创建变量表示农夫和牛的位置( a 为农夫, b 为牛, x 为行, y为列)
int lxa , lya , lxb , lyb ; //创建变量表示农夫和牛移动后的位置(移动可能不成立)
int fa = 1 , fb = 1 ; //创建变量表示农夫和牛的方向( 1 为北、 2 为东、 3 为南、 4 为西)
int ans = 0 ; //创建变量表示时间(输出结果)

//农夫移动函数:
void yda(){
	//平移区:
	if ( fa == 1 ){ //当方向为北时
		lxa = xa - 1 ; //行向上移动(减 1 )
		lya = ya ; //列不动
	}
	if ( fa == 2 ){ //当方向为东时
		lxa = xa ; //行不动
		lya = ya + 1 ; //列向右移动(加 1 )
	}
	if ( fa == 3 ){ //当方向为南时
		lxa = xa + 1 ; //行向下移动(加 1 )
		lya = ya ; //列不动
	}
	if ( fa == 4 ){ //当方向为西时
		lxa = xa ; //行不动
		lya = ya - 1 ; //列向左移动(减 1 )
	}
	//判定区:
	if ( lxa >= 1 && lya >= 1 && lya <= 10 && lxa <= 10 && mp[lxa][lya] == '.' ){ //当移动后位置在地图内且为空地
		xa = lxa ; //行位置
		ya = lya ; //列位置
	}
	else{ //不成立 
		fa++ ; //方向转变
		if ( fa > 4 ){ //当方向值大于西侧
			fa = 1 ; //变回北侧
		}
	}
}

//牛移动函数: 
void ydb(){
	//平移区
	if ( fb == 1 ){ //当方向为北时
		lxb = xb - 1 ; //行向上移动(减 1 )
		lyb = yb ; //列不动
	}
	if ( fb == 2 ){ //当方向为东时
		lxb = xb ; //行不动
		lyb = yb + 1 ; //列向右移动(加 1 )
	}
	if ( fb == 3 ){ //当方向为南时
		lxb = xb + 1 ; //行向下移动(加 1 )
		lyb = yb ; //列不动
	}
	if ( fb == 4 ){ //当方向为西时
		lxb = xb ; //行不动
		lyb = yb - 1 ; //列向左移动(减 1 )
	}
	//判定区:
	if ( lxb >= 1 && lyb >= 1 && lyb <= 10 && lxb <= 10 && mp[lxb][lyb] == '.' ){ //当移动后位置在地图内且为空地
		xb = lxb ; //行位置
		yb = lyb ; //列位置
	}
	else{ //不成立 
		fb++ ; //方向转变
		if ( fb > 4 ){ //当方向值大于西侧
			fb = 1 ; //变回北侧
		}
	}
}

//主函数: 
int main(){
	//输入区:
	for ( int i = 1 ; i <= 10 ; i++ ){ //基本二维输入行
		for ( int j = 1 ; j <= 10 ; j++ ){ //基本二维输入列
			cin >> mp[i][j] ; //输入
			if ( mp[i][j] == 'F' ){ //当输入为农夫位置时,将其存储至农夫的位置
				xa = i ; //储存行
				ya = j ; //储存列
				mp[i][j] = '.' ; //为判断方便,将其变为空地
			}
			if ( mp[i][j] == 'C' ){ //当输入为牛位置时,将其存储至牛的位置
				xb = i ; //储存行
				yb = j ; //储存列
				mp[i][j] = '.' ; //为判断方便,将其变为空地
			}
		}
	}
	//模拟区: 
	while(1){
		yda() ; //农夫移动
		ydb() ; //牛移动
		ans++ ; //时间增加
		if ( ans > 10000 ){ //此处为骗分,如果大于 10000 还是没有重合,即无法重合
			cout << 0 ; //直接输出 0
			return 0 ; //结束程序
		}
		if ( xa == xb && ya == yb ){ //如果重合(行列均相同)
			cout << ans ; //输出时间
			return 0 ; //结束程序
		}
	}
}
习题8-4 多项式输出
#include 
using namespace std;

int n, tmp;
bool first = true; // 引入一个 flag 标志变量来记录是否已经输出过一项,为True表示还在处理首项

int main() {
    scanf("%d", &n);
    for (int i = n; i >= 0; i--) {
        scanf("%d", &tmp);
        if (tmp == 0) continue;

        if (!first) {
            if (tmp > 0) printf("+");
            else printf("-");
        } else {
            if (tmp < 0) printf("-");
            first = false;
        }

        int absTmp = abs(tmp);

        if (i == 0) {
            printf("%d", absTmp);
        } else if (i == 1) {
            if (absTmp == 1) printf("x");
            else printf("%dx", absTmp);
        } else {
            if (absTmp == 1) printf("x^%d", i);
            else printf("%dx^%d", absTmp, i);
        }
    }

    // 如果所有项都是0
    if (first) printf("0");
    return 0;
}
习题8-5 字符串的展开
#include
using namespace std;
int p1,p2,p3,i=0,k;
char ch[300],be,af,f,j,p;//p用于输出; 
int main() {
	scanf("%d%d%d%s",&p1,&p2,&p3,ch);//输入;
	while(ch[i]){//当ch[i]有值时;
		be=ch[i-1];af=ch[i+1];f=ch[i];//f存储ch[i],便于判断; 
		if(f=='-'&&af>be&&(be>='0'&&af<='9'||be>='a'&&af<='z')){//意思是ch[i]若为'-',就判断其前后是否满足条件,满足就进入循环
			if (p3 == 1) { // 升序展开
                for (j = be + 1; j < af; j++) {
                    p = j;
                    if (p1 == 2 && p >= 'a' && p <= 'z') p = p - 32;
                    else if (p1 == 3) p = '*';
                    for (k = 0; k < p2; k++) printf("%c", p);
                }
            } else { // 降序展开
                for (j = af - 1; j > be; j--) {
                    p = j;
                    if (p1 == 2 && p >= 'a' && p <= 'z') p = p - 32;
                    else if (p1 == 3) p = '*';
                    for (k = 0; k < p2; k++) printf("%c", p);
                }
            }
            

		} 
		else
			printf("%c",f);//如果ch[i]是非'-'或者其前后不满足条件,就原样输出;
		i++;//一定要放在后面,不然会出错;
	}
	return 0;
}
习题8-6 作业调度方案

这道模拟题需要把题意先理解清楚后才能做,基本没用到什么算法,就是一个模拟题

#include 
using namespace std;

struct node{
	int num,tim;
}e[22][22];
int n,m;//n工件数 m机器数及工序数 
int mac[22][40000];
int ord[400];
int now[22];
int be[22];

int main()
{
	scanf("%d %d",&m,&n);
	int tmp=n*m;
	for(int i=1;i<=tmp;i++) scanf("%d",&ord[i]);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			scanf("%d",&e[i][j].num);
			
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			scanf("%d",&e[i][j].tim);
			
	int ans=0;
	for(int i=1;i<=tmp;i++)//大模拟 
	{
		int whi=ord[i];//第几个工件 
		int wha=(++now[whi]);//第几步骤
		
		int ma=e[whi][wha].num;//哪个机器 
		int go=e[whi][wha].tim;//多少时间
		
		int cnt=0,end;
		for(int j=be[whi]+1;;j++)
		{
			if(mac[ma][j]==0) cnt++;
			else cnt=0;
			if(cnt==go)
			{
				end=j;
				break;
			}
		}
		
		for(int j=end-go+1;j<=end;j++) mac[ma][j]=1;
		
		be[whi]=end;
		
		ans=max(ans,end);
	}
	
	printf("%d",ans);
	return 0;
}
习题8-7 帮贡排序

使用了结构体排序和自定义cmp排序,我把第九章放在第八章前也是为了这道题可以更容易理解一些

#include
using namespace std;
int n;
struct nihao{
	string na,zw,xzw;  // 名字,过去的位置,现在更新后的位置
	long long bg; // 帮贡
	int le,h; // 等级和序号
}ab[115];
int change(string a){
	if (a=="BangZhu") return 0;
	if (a=="FuBangZhu") return 1;
	if (a=="HuFa") return 2;
	if (a=="ZhangLao") return 3;
	if (a=="TangZhu") return 4;
	if (a=="JingYing") return 5;
	if (a=="BangZhong") return 6;
}
int cmp1(nihao x,nihao y){
	if (x.bg==y.bg) return x.h<y.h; // 帮贡相等按序号排序
	else return x.bg>y.bg; // 否则按帮贡来排
}
int cmp2(nihao x,nihao y){
	if (change(x.xzw)==change(y.xzw)){  
		if (x.le==y.le) return x.h<y.h;  //现职位相等且等级相等,按输入顺序排列
		return x.le>y.le;  //等级不等按等级排序 
	}
	return change(x.xzw)<change(y.xzw);  //现职位不等按现职位大小排序,使用自定义change函数,把职位赋值化,数字越小者越强
}

int main(){
	cin>>n;
	for (int i=1;i<=n;i++){
		cin>>ab[i].na>>ab[i].zw>>ab[i].bg>>ab[i].le;
		ab[i].h=i;
	}
	sort(ab+4,ab+1+n,cmp1);
	for (int i=1;i<=n;i++){
		if (i==1) ab[i].xzw="BangZhu";
		else if (i==2||i==3) ab[i].xzw="FuBangZhu";
		else if (i==4||i==5) ab[i].xzw="HuFa";
		else if (i>=6&&i<=9) ab[i].xzw="ZhangLao";
		else if (i>=10&&i<=16) ab[i].xzw="TangZhu";
		else if (i>=17&&i<=41) ab[i].xzw="JingYing";
		else ab[i].xzw="BangZhong";
	}
	sort(ab+1,ab+1+n,cmp2);
	for (int i=1;i<=n;i++){
		cout<<ab[i].na<<" "<<ab[i].xzw<<" "<<ab[i].le<<endl;
	}
    return 0;
}
习题8-8 阶乘数码

本题使用高精度乘单精度的板子加上数位统计即可

#include 
using namespace std;

int a[5000]; // 用于高精度,最多支持到几千位阶乘

int main() {
    int T;
    cin >> T;
    while (T--) {
        int n, d, len = 1;
        cin >> n >> d;
        a[0] = 1;

        // 计算 n!
        for (int i = 2; i <= n; i++) {
            int carry = 0;
            for (int j = 0; j < len; j++) {
                int temp = a[j] * i + carry;
                a[j] = temp % 10;
                carry = temp / 10;
            }
            while (carry) {
                a[len++] = carry % 10;
                carry /= 10;
            }
        }

        // 统计数字 d 出现次数
        int cnt = 0;
        for (int i = 0; i < len; i++)
            if (a[i] == d) cnt++;

        cout << cnt << endl;
    }
    return 0;
}
习题8-9和习题8-10会在后续的贪心算法和快速幂学完后进行补充,有兴趣的读者也可以自己尝试解决一下

习题8-9 最大乘积 P149
习题8-10 麦森数 P1045

你可能感兴趣的:(算法,c++,排序算法)