蛮力法——模拟与暴力

蛮力法


持续更新中 加个关注,后续上新不错过~

模拟(impelementation):

  • 通俗的讲就是:找不到更高效的做法时,题目怎么描述就让程序怎么运行
  • 模拟的特点:
    1. 思考量不大,但阅读量和代码量可以很大
    2. 可以很简单,也可以很复杂
    3. 形式多样:走迷宫,斗地主,打麻将,打三国杀,魔塔
  • 写模拟需要注意:
    1. 弄清题意,建立模型,注意细节

例题

1.约瑟夫环

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


分析

数组实现:
利用公式(p+m-1)%n,找出要输出数组的下标
其中p为开始报数的人的标号,n为当前剩下的人数


参考代码
#include 
void Jodeph(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;
        k++;
        printf("第%d个出圈的是:%d\n",k,a[p]);
        for(int j=p+1;j<n;j++){
            a[j-1]=a[j];   // 出圈后后边位置前移一个
        }
        n--; // 总数减一
        if(p==n){
            p=0;   // 这部分去掉,最后一行a[p]改成a[0],貌似也行
        }
    }
    printf("最后剩下的是:%d\n",a[p]);
}
int main()
{
    int n,m;
    n=7;
    m=3;
    Jodeph(n,m);
}

输出样例

蛮力法——模拟与暴力_第1张图片



2.Online judge


给定两个输入,分别表示用户的输出和正确的输出,你来模拟评测机,对两个输出进行比较完全相同输出“Accepted”只有空格,tab,回车的不同,输出“Presentation Error”否则输出“Wrong Answer”


Sample Input


4
START
1 + 2 = 3
END
START
1+2=3
END
START
1 + 2 = 3
END
START
1 + 2 = 3
END START
1 + 2 = 3
END
START
1 + 2 = 4
END START
1 + 2 = 3
END START
1 + 2 = 3
END


Sample Output


Presentation Error
Presentation Error
Wrong Answer
Presentation Error


解题思路

直接模拟比较过程即可
我们可以先将输入的两组数据去掉’\n’,’\t’和’ ‘,得到新的两组数据,那么这两组数据如果不是完全匹配的,结果一定是WA。如果这两组匹配了,去掉’\n’,’\t’和’ '前的不完全匹配则是PE。如果去掉前的两组数据完全匹配,则是AC。

注意:gets(),在读入时,若遇到’\n’,会将其扔掉。而文本中可能会出现’\n’,所以可以在每读入一行是在字符串的后边加上‘\n’


参考代码
#include
#include
const int MAXN=5050;
char a1[MAXN],a2[MAXN];
char b1[MAXN],b2[MAXN];
char temp[MAXN];
void input(char a[],char b[])
{
    gets(temp);
    while(strcmp(temp,"START")!=0)gets(temp);

    while(gets(temp))
    {
        if(strcmp(temp,"END")==0)break;
        if(strlen(temp)!=0)strcat(a,temp);  //若该行为'\n',则不需要进行此操作
        strcat(a,"\n");
    }
    int t=0;
    int len=strlen(a);
    for(int i=0;i<len;i++)
    {
        if(a[i]!=' '&&a[i]!='\t'&&a[i]!='\n')
          b[t++]=a[i];
    }
    b[t]='\0';
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        a1[0]='\0';
        a2[0]='\0';
        input(a1,b1);
        input(a2,b2);
        if(strcmp(a1,a2)==0)printf("Accepted\n");
        else if(strcmp(b1,b2)==0)printf("Presentation Error\n");
        else printf("Wrong Answer\n");
    }
    return 0;
}



3.False Coin

给你n个硬币,K组称量结果。在这n个硬币中有一个是假的,它的重量跟其他的不一样(不知道大小),真硬币的重量都相同。每组第一个数字代表左右放置硬币的数量,后边则是硬币的编号,每组后边的符号则是称量结果。问能不能确定哪个是假币,如果不能输出0

Input

The first line of the input file contains two integers N and K, separated by spaces, where N is the number of coins (2<=N<=1000 ) and K is the number of weightings fulfilled (1<=K<=100). The following 2K lines describe all weightings. Two consecutive lines describe each weighting. The first of them starts with a number Pi (1<=Pi<=N/2), representing the number of coins placed in the left and in the right pans, followed by Pi identifiers of coins placed in the left pan and Pi identifiers of coins placed in the right pan. All numbers are separated by spaces. The second line contains one of the following characters: ‘<’, ‘>’, or ‘=’. It represents the result of the weighting:
‘<’ means that the weight of coins in the left pan is less than the weight of coins in the right pan,
‘>’ means that the weight of coins in the left pan is greater than the weight of coins in the right pan,
‘=’ means that the weight of coins in the left pan is equal to the weight of coins in the right pan.


Output

Write to the output file the identifier of the false coin or 0, if it cannot be found by the results of the given weightings.

Sample Input


5 3
2 1 2 3 4
<
1 1 4
=
1 2 5
=

Sample Output


3


解题思路

结果为等号,两边一定是真硬币,结果为不等号肯定含有假硬币
所以对于所有的不等式,将轻硬币放在lightCoins里,将重硬币放在heavyCoins里
因为只有一个假硬币,且假硬币不会一会轻,一会重,所以假硬币的出现在lightCoins或者heavyCoins的次数一定和不等号出现的次数相等
若恰有一个满足条件的疑似假币,则输出,若有多个或没有,则输出0。



参考代码
#include 
#include 
int f[1010];
int book[1010];
int p[1010];
int main()
{
    int n,m,k;
    while(~scanf("%d%d",&n,&k)){
        int ans=0;
        memset(f,0,sizeof(f));
        memset(book,0,sizeof(book));
        while(k--){
            scanf("%d",&m);
            for(int i=0;i<2*m;i++){
                scanf("%d",&p[i]);
            }
            getchar();
            char c=getchar();
            if(c=='='){
                for(int i=0;i<2*m;i++){
                    book[p[i]]=1;   // 记录真硬币
                }
            }
            else if(c=='<'){
                ans++;
                for(int i=0;i<m;i++){
                    f[p[i]]--;  // 疑似轻硬币
                }
                for(int i=m;i<2*m;i++){
                    f[p[i]]++;  // 疑似重硬币
                }
            }
            else{
                ans++;
                for(int i=0;i<m;i++){
                    f[p[i]]++;
                }
                for(int i=m;i<2*m;i++){
                    f[p[i]]--;
                }
            }
        }
        int ces=0,flag;
        for(int i=1;i<=n;i++){
            if(!book[i]){
                if(f[i]==ans||f[i]==-ans){
                    ces++;
                    flag=i;
                }
            }
        }
       if(ces==1){
            printf("%d\n",flag);  // 若恰有一个满足条件的疑似硬币,则输出
        }
        else{
            printf("0\n");  // 多个或没有,则输出0
        }
    }
    return 0;
}



暴力

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


    蛮力法——模拟与暴力_第2张图片

蛮力法——模拟与暴力_第3张图片

1、Sn>k


已知Sn=1+1/2+1/3+…+1/n.给出一个整数k(1<=k<=15)计算出一个最小的n,使得Sn>k
分析:
Sn为无穷级数,不收敛,对于任意K,总能找到一个Sn>k
写一个程序算一下发现S1835421>15,不用担心超时
所以直接暴力即可

int query(int k)
{
    double sum=0;
    for(int i=1;;i++){
        sum+=1.0/i;
        if(sum>k){
            return i;
        }
    }
}

如果每个用例有q(1<=q<=100000)
每次都要重新算,太浪费时间,预处理

int ans[20];
for(int i=1;i<=15;i++){
    ans[i]=quert(i);
}


2、百钱买百鸡问题


有一个人,有一百块钱,打算买一百只鸡
到市场一看,公鸡一只3元,母鸡一只5元,小鸡3只1元
试求用100元买100只鸡,各为多少才合适?输出所有方案


解题思路

根据题意我们可以得到方程组
3X+5Y+Z/3=100
X+Y+Z=100
问题转化为求不定方程的整数解
for 枚举X,把X看做已知,解出Y,Z
Y=(200-8X)/14
Z=(1200-6X)/14

参考代码
#include 
int main()
{
    for(int x=0;x<=25;x++){
        if((200-8*x)%14==0&&(1200-6*x)%14==0){
            printf("%d %d %d\n",x,(200-8*x)/14,(1200-6*x)/14);
        }
    }
    return 0;
}


输入输出样例

蛮力法——模拟与暴力_第4张图片


3.Division

输入正整数n(2<=n<=79),按照从小到大的顺序,输出所有形如abcde/fghij=n 表达式,其中 a - j 恰好为数字0~9的一个排列(可以有前导0)

Sample Input:62

Sample Output: 79546/01283=62 94736/01528=62


解题思路
  • 可不可以枚举0-9的所有排列?
  • 一共需要枚举10!=3628800次,可以接受,但是没必要
  • 只需枚举fghij就可以算出abcde,再判断是否所有数字都不相同即可
  • 枚举次数减少到1万以内


参考代码
#include 
#include 
int cmp1(const void* a,const void* b){
  return (*(int*)a-*(int*)b);
}

int change(int a,int b)
{
    int s[10];
    for(int i=0;i<10;){
        s[i++]=(a%10);
        a/=10;
        s[i++]=(b%10);
        b/=10;
    }
    qsort(s,10,sizeof(int),&cmp1);
    for(int i=0;i<10;i++){
        if(s[i]!=i){
            return 0;
        }
    }
    return 1;
}

int main()
{
    int N,flag=0;
    while(scanf("%d",&N)&&N!=0){
        if(flag){
            printf("\n");
        }
        flag++;
        int k=1;
        for(int i=1234;i<98765;i++){
            int j=i*N;
            if(j>=10234&&j<=98765){
                if(change(i,j)==1){
                    k=0;
                    if(i>10000){
                        printf("%d / %d = %d\n",j,i,N);
                    }
                    else{
                        printf("%d / 0%d = %d\n",j,i,N);
                    }
                }
            }
        }
        if(k){
            printf("There are no solutions for %d.\n",N);
        }
    }
    return 0;
}



4、Four Operations

给定一个全是数字的字符串(长度为5到20),按照顺序加入"+","-","*","/"四个运算符,求最大的计算结果,有q组询问(1<=q<=100000)

Sample Input:12345

Sample Output:1


解题思路
  • 直接暴力会超时,约 2 0 4 20^4 204种情况
  • 计算形式总是a+b-c*d/e,我们希望a,b,e大一些,c和d小一些
  • 给c和d各一位,给e一位或两位,a和b中的一个拿一位,另一个拿光
  • 共枚举四种情况即可

参考代码
#include 
#include 
int main()
{
    int T;
    char s[25];
    long long a,b,c,d,e,a_1,b_1,c_1,d_1,e_1,i,len,b_2,a_2,max_1,max_2,a_3,b_3,a_4,b_4;
    long long max_12,max_21,max=0;
    int count=1;
    scanf("%d",&T);
    while(T--){
        scanf("%s",s);
        len=strlen(s);
        if(len==5){
            a=s[0]-'0';
            b=s[1]-'0';
            c=s[2]-'0';
            d=s[3]-'0';
            e=s[4]-'0';
            printf("Case #%d: %lld\n",count,a+b-c*d/e);
            count++;
        }
        else{
            e_1=s[len-1]-'0';
            d_1=s[len-2]-'0';
            c_1=s[len-3]-'0';
            max_12=c_1*d_1/e_1;
            a_1=s[0]-'0';
            b_1=0;
            for(i=1;i<len-3;i++){
                b_1=b_1*10+(s[i]-'0');
            }
            b_2=s[len-4]-'0';
            a_2=0;
            for(i=0;i<len-4;i++){
                a_2=a_2*10+(s[i]-'0');
            }
            max_1=a_1+b_1;
            if(max_1<(a_2+b_2)){
                max_1=(a_2+b_2);
            }
            e_1=(s[len-2]-'0')*10+s[len-1]-'0';
            d_1=s[len-3]-'0';
            c_1=s[len-4]-'0';
            max_21=c_1*d_1/e_1;
            a_3=s[0]-'0';
            b_3=0;
            for(i=1;i<(len-4);i++){
                b_3=b_3*10+(s[i]-'0');
            }
            b_4=s[len-5]-'0';
            a_4=0;
            for(i=0;i<(len-5);i++){
                a_4=a_4*10+(s[i]-'0');
            }
            max_2=(a_3+b_3);
            if(max_2<(a_4+b_4)){
                max_2=(a_4+b_4);
            }
            max=max_1-max_12;
            if(max<max_2-max_21){
                max=(max_2-max_21);
            }
            printf("Case #%d: %lld\n",count,max);
            count++;
        }
    }
    return 0;
}


5 白学串

龙老师特别擅长出各种奇奇怪怪而且很难的题目。今天龙老师又想了一道好玩的题目。
白学家龙老师有一个数字序列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。


解题思路

不难发现,一组数不能构成三角形的充要条件是排完序后任意三个相邻的数构不成三角形
换句话说,要写出尽可能多的数就要让相邻的三个数列刚好构不成三角形
不能构成三角形的极限情况就是斐波拉契数列
1,1,2,3,5,8,13,21…
而斐波拉契数列增长很快,约第40项就达到了1e9,而题目给的数不超过1e9
所以当区间长度超过40,输出YES,否则排序后判断或直接暴力
斐波那契数之间是无法构成三角形;所以我们可以尝试列举一下。

列举结果如下:

  1. 1
  2. 1
  3. 2
  4. 3
  5. 5
  6. 8
  7. 13
  8. 21
  9. 34
  10. 55
  11. 89
  12. 144
  13. 233
  14. 377
  15. 610
  16. 987
  17. 1597
  18. 2584
  19. 4181
  20. 6765
  21. 10946
  22. 17711
  23. 28657
  24. 46368
  25. 75025
  26. 121393
  27. 196418
  28. 317811
  29. 514229
  30. 832040
  31. 1346269
  32. 2178309
  33. 3524578
  34. 5702887
  35. 9227465
  36. 14930352
  37. 24157817
  38. 39088169
  39. 63245986
  40. 102334155
  41. 165580141
  42. 267914296
  43. 433494437
  44. 701408733
  45. 1134903170
  46. 1836311903
  47. 2971215073
  48. 4807526976
  49. 7778742049
  50. 12586269025

在第40项开始超过题目给定的范围,所以当询问的区间大于40,则必定能构成三角形;剩下的则暴力排序判断。


参考代码
#include 
#include 
#include 
const int N=1e5+5;
int a[N],b[45];
int cmp1(const void* a,const void* b){
  return (*(int*)a-*(int*)b);
}
bool judge(int z,int r)
{
    int len=r-z+1;
    int i;
    if(len<3){
        return false;
    }
    for(i=0;i<len;i++){
        b[i]=a[z+i-1];
    }
    qsort(b,len,sizeof(int),&cmp1);
    for(int i=2;i<len;i++){
        if(b[i]<b[i-1]+b[i-2]){
            return true;
        }
    }
    return false;
}

int main()
{
    int t,m,n,z,r,i;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&m,&n);
        for(int i=0;i<m;i++){
            scanf("%d",&a[i]);
        }
        for(i=1;i<=n;i++){
            scanf("%d %d",&z,&r);
            if(r-z+1>40){
                printf("yes\n");
            }
            else if(judge(z,r)){
                printf("yes\n");
            }
            else{
                printf("no\n");
            }
        }
    }
    return 0;
}



若有帮助的话,请点个赞吧!

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