模拟&&暴力

模拟

许多现象不具有确定的性质,有些问题甚至很难建立数学模型,或者很难用计算机建立递推,递归,枚举,回溯等算法,在这种情况下,一般采用模拟策略,而模拟策略也就是模拟某个过程,通过改变数学模型的各种参数,进而观察变更这些参数所引起过程状态的的变化,由此展开算法设计,通俗的说找不到更高效的算法,题目怎么描述怎么运行。

特点:

思考量不大,但阅读量和代码量可以很大,可以很简单,也可以很复杂,形式多样,走迷宫,斗地主,打麻将,打三国杀,魔塔。

需要注意点:

弄清题意,建立模型,注意细节

经典例题:

约瑟夫环问题:

已知N个人(以编号1、2、3…n分别表示)围坐在一张圆桌周围,从编号为1的人开始报数,数到m的人出列,他的下一个人有从1开始报数,数到m的人又出列,依次规律重复下去,直到员桌周围的人全部出列,输出出队序列和最后剩下的人。

void Joseph(int n,int m){
    int a[100],k=0,p=0;
    for(int i=0;i<n;i++){
        a[i]=i+1;
    }
    while(n>1){
        p=(p+m-1)%n;
        cout<<++k<<"  "<<a[p]<<endl;
        for(int j=p+1;j<n;j++){
            a[j-1]=a[j];
        }
        n--;
        if(p==n) p=0;
    }
    cout<<a[p]<<endl;
}

类似小时候玩的点兵点将
2.OnlineJudge (HDU-1073)模拟测评机
完全相同accepted
只有空格tab回车不同,presentationError
否则wrongAnswer
分析

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
#define maxn 5555
char t[maxn],a[maxn],aa[maxn],b[maxn],bb[maxn];
void deal(char *a,char *aa){
    gets(t);
    while(strcmp(t,"START")!=0) gets(t);
    while(gets(t)){
       if(strcmp(t,"END")==0)break;
       if(strlen(t))strcat(a,t);
       else strcat(a,"\n");
    }
    int res=0,len=strlen(a);
    for(int i=0;i<len;i++){
        if(a[i]!=' '&&a[i]!='\n'&&a[i]!='\t') aa[res++]=a[i];
    }
    aa[res]='\0';
}
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        a[0]='\0';b[0]='\0';
        deal(a,aa);deal(b,bb);
        if(strcmp(a,b)==0)printf("Accepted\n");
        else if(strcmp(aa,bb)==0)printf("Presentation Error\n");
        else printf("Wrong Answer");
    }
    system("pause");
    return 0;
}

运行结果:
在这里插入图片描述
3.False Coin(POJ-1029)
题意:给n个硬币,其中有一个硬币和其他的硬币重量不一样,给出k次比较重量的结果。问是否可以将假硬币找出来。
分析:
1、结果为等号两边一定为真币,结果不等则一定含有假币
2、所以对于所有的不等式,将轻硬币放到lightCoins中,将重硬币放到heavyCoins中
3、因为只有一个假硬币,且假硬币不会一会轻一会重,所以最后假硬币的出现在lightCoins或者HeavyCoins的次数一定和不等号次数相等
4、若恰有一个满足条件的疑似假币,则输出。

#include<bits/stdc++.h>
#define FALSE 0
#define TRUE 1
using namespace std;
/*
 该题目主要的模拟,不涉及数据结构和算法的相关知识。
 因为还有=号的式子一定是真硬币,而还有大于号和小于号的式子肯定含有假硬币
 所以对于所有的不等式,将轻硬币放在lightCoins里,将重硬币放在heavyCoins里,
 因为只有一个假硬币,所以最后假硬币的出现在lightCoins或者HeavyCoins的次数一
 定和不等式的次数相等
*/
int main(){
    int n, k;
    while (cin >> n >> k){
        vector<int> coins(n+1, FALSE);//设置大小为n+1的数组,并且设置所有的coin为false
        vector<int> lightCoins(n + 1, 0);//初始化轻硬币出现的次数为0
        vector<int> heavyCoins(n + 1, 0);//初始化重硬币出现的次数为1

        int noEqualNums = 0;//是不等式出现的次数

        for (int i = 0; i < k; i++){
            int nums;
            cin >> nums;
            vector<int> tempCoins;
            for (int j = 0; j < 2 * nums; j++){
                int x;
                cin >> x;
                tempCoins.push_back(x);
            }
            char c;
            cin >> c;
            if (c == '='){
                for (int j = 0; j < 2 * nums; j++){
                    coins[tempCoins[j]] = TRUE;
                }
            }
            else if (c=='<'){//则右边的硬币都是正确的
                for (int j = 0; j < nums; j++){
                    lightCoins[tempCoins[j]]++ ;//左边的放在轻硬币中
                }
                for (int j = nums; j < 2 * nums; j++){
                    heavyCoins[tempCoins[j]]++ ;//重的放在重硬币中
                }
                noEqualNums++;
            }
            else{
                for (int j = 0; j < nums; j++){
                    heavyCoins[tempCoins[j]]++;
                }
                for (int j = nums; j < 2 * nums; j++){
                    lightCoins[tempCoins[j]]++;
                }
                noEqualNums++;
            }
        }

        int ansNums=0;
        int ans;
        for (int i = 1; i <= n; i++){
            if (coins[i] == FALSE&&(heavyCoins[i]==noEqualNums||lightCoins[i]==noEqualNums)){  //满足条件
                ansNums++;
                ans = i;
            }
        }

        if(ansNums == 1){
            cout << ans << endl;//如果最后的答案只要一个,则输出
        }
        else{
            cout << 0 << endl;
        }
    }
    system("pause");
    return 0;
}

暴力brute force

暴力就是枚举,指的是从问题所有可能的解的集合中一一枚举各元素。
用题目中给定的检验条件判断哪些是无用的,哪些是有用的,能使命提成立,即为其解。
优点,算法简单,在局部使用枚举法,效果比较好
缺点,运算量过大,当问题规模变大的时候,循环的结束变大,循环速度变慢,计算量容易过大。
所以需要先计算时间复杂度。
1.百元百鸡问题:

#include<bits/stdc++.h>
using namespace std;
int main(){
    int i,j,k;
    for(i=0;i<=34;i++){
        if((200-8*i)%14==0&&(1200-6*i)%14==0&&(200-8*i)/14>=0&&(1200-6*i)/14>=0)
        cout<<i<<' '<<(200-8*i)/14<<' '<<(1200-6*i)/14<<endl;
    }
    system("pause");
}

在这里插入图片描述
直接暴力,再优化
2.UVA 725 Division
题目:给你一个数字n,用0~9,10个数字组成两个五位数,使得他们的商为n,按顺序输出所有结果。

分析:暴力。直接枚举第二个数字,范围(1000,100000),然后判断即可。

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

int used[10];

int judge(int a,int b){
    if(b > 98765) return 0;
    for(int i = 0;i < 10;++i) 
        used[i]=0;
    if(a<10000) used[0]=1;
    while(a){
        used[a%10]=1;
        a/=10;
    }
    while(b){
        used[b%10]=1;
        b/=10;
    }
    int sum=0;
    for(int i=0;i<10;++i) 
       sum+=used[i];
    return (sum==10);
}

int main(){
    int n,T=0;
    while(~scanf("%d",&n)&&n){
        if(T++) printf("\n");
        int count=0;
        for(int i=1234;i<100000;++i){
            if(judge(i,i*n)){
                printf("%05d/%05d=%d\n",i*n,i,n);
                count++;
            }
        }
        if(!count) printf("There are no solution for %d.\n",n);
    }
    system("pause");
    return 0;
}

==分析:==可不可以枚举0~9的所有排列?
一共需要枚举10!=3628800次,emmmmm,可以接受,但是没必要,只需要枚举fghij就可以算出abcde,在判断是否所有数字都不相同即可
枚举次数减少到(5!*(79-2))==9240之间了
3.Four Operations(HDU-5938)

给出一串由1-9数字组成的数字串(5<=len<=20),往其中按"顺序"添加+,-,,/”,将其分成5个数,使其运算结果最大。
直接暴力会超时,(20^4))
==思路:==因为有-,所以
和/后的结果要尽可能小,一位数*一位数最大只能是两位数,所以除数就可能为一位数或两位数,如果是三位数,那么会产生小数,从而浪费了前面的加和。还有就是前面相加的两个数可能是所有数+最后一位(相对而言)或第一位+后面的全部。四种情况比较一下大小即可。

计算形式是:a+b-c*d/e;我们希望a,b,e尽可能的大,而c,d尽可能的小.
**分配策略:**给c,d各一位,给e一位或者两位,a和b中一个拿一位,另一个拿光,=====>只需要枚举四种情况,给e一位还是两位,ab中谁拿多的。

#include<bits/stdc++.h>
using namespace std;
string a;
const int INF=0x3f3f3f3f;
int b[100];
 
long long pfh(int n,int m)
{
	long long sum=0;
	for(int i=n;i<=m;i++)
	{
             sum*=10//pow会强转
	     sum+=b[i];
	}	
return sum;
}
 
int main()
{
	int t;
	scanf("%d",&t);
	int flag=0;
	while(t--)
	{
		cin>>a;//string只能用cin输入 
		long long len,ans=-INF,c,d,e,add;
		len=a.size();
		for(int i=0;i<len;i++)
			b[i+1]=a[i]-'0';//字符串一定和asc码有关
				 
		for(int i=2;i<=len-3;i++)
		{
			add=max((pfh(1,i-1)+pfh(i,i)),(pfh(1,1)+pfh(2,i)));//求加法 
			c=b[i+1];//枚举减号位置,将后面数进行乘除比较 
			d=b[i+2];
			e=pfh(i+3,len);
			ans=max(ans,add-c*d/e);
		}
		printf("Case #%d: %lld\n",++flag,ans);	
	}
return 0;	
}
 

4、白学串(北理工新生赛18届H题)口区
龙老师特别擅长出各种奇奇怪怪而且很难的题目。今天龙老师又想了一道好玩的题目。
白学家龙老师有一个数字序列 a1, a2, · · · , an,定义这个序列的子串是序列中的连续一段,也就是ax, ax+1, · · · , ay−1, ay (1 ≤ x ≤ y ≤ n)。定义一个子串是白学的,当且仅当这个子串中存在 3 个元素ai, aj , ak (x ≤ i, j, k ≤ y) 能够以它们为边长组成一个合法的三角形。现在龙老师想知道,对于他询问的每个子串,那些是白学串。
Input
第一行输入一个正整数 T (1 ≤ T ≤ 100),表示数据组数。输入保证 ∑n ≤ 5 × 105,∑m ≤ 106。
接下来 T 组数据,每组数据第一行输入两个正整数 n, m (1 ≤ n ≤ 105, 1 ≤ m ≤ 2 × 105) 由空格分
隔开,分别表示数字序列长度和询问数。
第二行输入 n 个正整数,由空格间隔开,其中第 i 个正整数是 ai (1 ≤ ai ≤ 108),描述这个序列。
接下来 m 行,每行输入两个正整数 l 和 r (1 ≤ l ≤ r ≤ n) 由空格分隔开,表示龙老师想问你:从第
l 个到第 r 个数组成的子串 a[l · · · r] 是不是白学的。
Output
对于每组数据,请输出 m 行,表示 m 个询问的答案。如果这个子串是白学的,请输出 yes,否则
请输出 no。
Example
standard input
2
5 3
1 1 2 3 4
1 3
2 4
2 5
5 2
2 3 4 1 2
1 4
3 5
standard output
no
no
yes
yes
no
题解
斐波那契数之间是无法构成三角形;所以我们可以尝试列举一下。
列举结果如下:
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765
10946
17711
28657
46368
75025
121393
196418
317811
514229
832040
1346269
2178309
3524578
5702887
9227465
14930352
24157817
39088169
63245986
102334155
165580141
267914296
433494437
701408733
1134903170
1836311903
2971215073
4807526976
7778742049
12586269025
在第51项开始超过题目给定的范围,所以当询问的区间大于50,则必定能构成三角形;剩下的则暴力排序判断。
输出建议用scanf和printf,而不是用cin和cout,因为打印过多,用cin和cout浪费效率。
在这里插入图片描述

#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+5;
int a[N],b[51];
bool judge(int z,int r){
	int len=r-z+1;
	if(len<3)
		return false; 
	for(int i=0;i<len;i++)
		b[i]=a[z+i-1];
	sort(b,b+len);
	for(int i=2;i<len;i++)
		if(b[i]<b[i-1]+b[i-2]) 
			return true;
	return false;
}
int main(){
	int t;
	cin>>t;
	while(t--){
		int m,n,z,r;
		cin>>m>>n;
		for(int i=0;i<m;i++)
			scanf("%d",&a[i]);
		for(int i=1;i<=n;i++){
			scanf("%d %d",&z,&r); 
			if(r-z+1>51)
				printf("yes\n");
			else if(judge(z,r))
				printf("yes\n");
			else
				printf("no\n");
		}
	}
	return 0;
}

QAQ

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

.0-0.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值