第一次训练 密码:acmore

  1 #include <cstdio>

  2 #include <cstring>

  3 #define M 100010

  4 #define INF 0x7FFFFFFF

  5 #define Min(a,b) (a < b ? a : b)

  6 #define mem0(f) memset(f,0,sizeof(f))

  7 char a[6][6];

  8 char b[6][6];

  9 int num[6][6];

 10 

 11 int get_num(int a,int b,int c,int d)

 12 {

 13     int number = 0;

 14     for(int i = 1; i <= 4; i++)

 15     {

 16         if((a & 1 << (i - 1)) == 0)    //位运算,1<<(i-1)=(i-1)*2

 17         {

 18             number++;

 19             num[1][i]++;

 20             num[1][i - 1]++;

 21             num[1][i + 1]++;

 22             num[2][i]++;

 23         }

 24         if((b & 1 << (i - 1)) == 0)

 25         {

 26             number++;

 27             num[2][i]++;

 28             num[2][i - 1]++;

 29             num[2][i + 1]++;

 30             num[1][i]++;

 31             num[3][i]++;

 32         }

 33         if((c & 1 << (i - 1)) == 0)

 34         {

 35             number++;

 36             num[3][i]++;

 37             num[3][i - 1]++;

 38             num[3][i + 1]++;

 39             num[2][i]++;

 40             num[4][i]++;

 41         }

 42         if((d & 1 << (i - 1)) == 0)

 43         {

 44             number++;

 45             num[4][i]++;

 46             num[3][i]++;

 47             num[4][i + 1]++;

 48             num[4][i - 1]++;

 49         }

 50     }

 51     return number;

 52 }

 53 

 54 void flip()

 55 {

 56     for(int i = 1; i <= 4; i++)

 57         for(int j = 1; j <= 4; j++)

 58             if(num[i][j] % 2 != 0)

 59             {

 60                 if(b[i][j] == 'w')

 61                     b[i][j] = 'b';

 62                 else

 63                     b[i][j] = 'w';

 64             }

 65 }

 66 

 67 int checkb()

 68 {

 69     int flag1 = 0;

 70     int flag2 = 0;

 71     for(int i = 1; i <= 4; i++)

 72         for(int j = 1; j <= 4; j++)

 73             if(b[i][j] == 'w')

 74                 return 0;

 75     return 1;

 76 }

 77 

 78 int checkw()

 79 {

 80     int flag1 = 0;

 81     int flag2 = 0;

 82     for(int i = 1; i <= 4; i++)

 83         for(int j = 1; j <= 4; j++)

 84             if(b[i][j] == 'b')

 85                 return 0;

 86     return 1;

 87 }

 88 

 89 int main()

 90 {

 91     int ans = INF;

 92     for(int i = 1; i <= 4; i++)

 93         scanf("%s",a[i] + 1);

 94     for(int i = 0; i < 16; i++)

 95         for(int j = 0; j < 16; j++)

 96             for(int k = 0; k < 16; k++)

 97                for(int p = 0; p < 16; p++)

 98                 {

 99                     memcpy(b,a,sizeof(a));

100                     mem0(num);

101                     int number = get_num(i,j,k,p);

102                     flip();

103                     if(checkb() || checkw())

104                         ans = Min(ans,number);

105                 }

106     if(ans == INF)

107         printf("Impossible\n");

108     else

109     printf("%d\n",ans);

110     return 0;

111 }

 

网址:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=26732#overview

先讲一下整体情况,A,B题是差不多的题型,DFS,A题枚举也行一共2^16种可能。

C题没什么好说的,暴力求解。

D题,待我再研究下题目。

E题,貌似数据有问题.....

F题,本来用母函数做,结果一句:Your program should be able to handle up to 100 coins.差点秒杀全场,结果证明可以用暴力求解,另DP。

总的一句:暴力专场!!!!!!

A题,4X4的黑白翻转棋,输出最小的能全变成一种颜色的步数,不能则输出:impossible。

#include<stdio.h>

#include<iostream>

using namespace std;

int chess[4][4];

int c=33;

void build()//将棋盘的颜色以标记化

{

    char c;

    int i,j;

    for(i=0;i<4;i++)

    for(j=0;j<4;j++)

    {

        cin>>c;

        if(c=='w')

        chess[i][j]=0;

        else

        chess[i][j]=1;

    }

}

void turn(int x,int y)//翻转

{

     if(x>=0&&x<=3&&y>=0&&y<=3)

     chess[x][y]=!chess[x][y];

}

void flip(int s)//一个棋子变化,周围四个都要变化

{

    int i=s/4;//

    int j=s%4;//

    turn(i,j);

    turn(i+1,j);

    turn(i,j+1);

    turn(i-1,j);

    turn(i,j-1);

}

int complete()//判断棋盘是否变成同一的颜色

{

    int i,j,s1=0;

    for(i=0;i<4;i++)

       for(j=0;j<4;j++)

          s1+=chess[i][j];

    if(s1%16)

      return 0;

    else

      return 1;

}

void dfs(int s,int b)//进行深搜.s代表当前的方格,b代表翻转的方格数

{

     if(complete())//如果是同一颜色

     {

         if(c>b)

           c=b;

        return;

     }

     if(s>=16)//如果遍历完

        return;

    dfs(s+1,b);  //不是很理解这一步及以下3步

      flip(s);

    dfs(s+1,b+1);

    flip(s);

}

int main()

{

    build();//将棋盘的颜色以标记化

    dfs(0,0);

    if(c==33)//由于翻转次数最多为4*4*2=32次

      printf("Impossible\n");

    else

      printf("%d\n",c);

    return 0;

}

上面这种方法待我看过DFS再来看,再做标注。
另一种代码:

#include<iostream>

#include<cstdio>

using namespace std;



int map[4][4],step,flag=0;



void turn(int i,int j)//转换

{

    map[i][j]=!map[i][j];

    if(i>0)

        map[i-1][j]=!map[i-1][j];

    if(i<3)

        map[i+1][j]=!map[i+1][j];

    if(j>0)

        map[i][j-1]=!map[i][j-1];

    if(j<3)

        map[i][j+1]=!map[i][j+1];

}



int range()//判定表格是否全部一样

{

    int i,j;

    for(i=0;i<4;i++)

        for(j=0;j<4;j++)

            if(map[i][j]!=map[0][0])

                return 0;

    return 1;

}



int DFS(int i,int j,int dp)//深搜

{

    if(dp==step)

    {

        flag=range();

        return 0;

    }

    if(flag||i==4)  return 1;

    turn(i,j);

    if(j<3)   

        DFS(i,j+1,dp+1);

    else 

        DFS(i+1,0,dp+1);

    turn(i,j);

    if(j<3)

        DFS(i,j+1,dp);

    else 

        DFS(i+1,0,dp);

    return 0;

}

int main()

{

    int i,j;

    char a;

    for(i=0;i<4;i++)

    {

        for(j=0;j<4;j++)

        {

            scanf("%c",&a);

            if(a=='b')

                map[i][j]=0;

            else 

                map[i][j]=1;

        }

        getchar();

    }

    for(step=0;step<=16;step++)

    {

        flag=0;

        DFS(0,0,0);

        if(flag)   break;

    }

    if(flag)

        printf("%d\n",step);

    else 

        printf("Impossible\n");

    return 0;

}

等我看了DFS再来写。。。。Orz

另一种,用枚举+位运算:

/*

解题思路:

由于输入的字符串中只含'b'或'w',而且只有16个字符,所以可以将输入的字符串理解为二进制表达式;

将这个字符串转换成一个10进制数字。然后bfs的同时,再用位运算(^)来改变它周围的数字(字符)。

具体见程序:

*/

#include <stdio.h>

#include <queue>

#include <string.h>

#include <stdlib.h>

#include <algorithm>

using namespace std;

struct node

{

    int step;

    int val;

};

int st,used[100000];

int bfs()

{

    int i;

    queue<node> q;

    node tmp;

    tmp.step=0;

    tmp.val=st;

    q.push(tmp);

    memset(used,0,sizeof(used));

    used[st]=1;

    while(!q.empty())

    {

        node cur=q.front();

        q.pop();

        if(cur.val==0||cur.val==(1<<16)-1)

            return cur.step;

        for(i=0;i<16;i++)

        {

            node next=cur;

            int tx=i/4,ty=i%4;

            //下面的4个if就是用来判断是否越界,如果没有,那么改变该节点(中心节点的邻节点);

            if(tx-1>=0) next.val^=1<<(15-(4*(tx-1)+ty));

            if(tx+1<4) next.val^=1<<(15-(4*(tx+1)+ty));

            if(ty-1>=0) next.val^=1<<(15-(4*tx+ty-1));

            if(ty+1<4) next.val^=1<<(15-(4*tx+ty+1));

            next.val^=1<<(15-i);

            if(!used[next.val])

            {

                next.step++;

                used[next.val]=1;

                q.push(next);

            }

        }

    }

    return -1;

}

int main()

{

    int i;

    char s[20];

    while(scanf("%s",s)!=EOF)

    {

        st=0;

        for(i=1;i<4;i++) scanf("%s",s+4*i);

        //将输入的字符串转换成一个10进制数。

        for(i=0;i<16;i++)

        {

            st<<=1;

            st|=(s[i]=='b');

        }

        int ans=bfs();

        if(ans==-1) printf("Impossible\n");

        else printf("%d\n",ans);

    }

    return 0;

}

 枚举+位运算:这个比较好理解  1 #include <cstdio>

 2 #include <cstring>

 3 #define M 100010

 4 #define INF 0x7FFFFFFF

 5 #define Min(a,b) (a < b ? a : b)

 6 #define mem0(f) memset(f,0,sizeof(f))

 7 char a[6][6];  8 char b[6][6];  9 int num[6][6];  10 

 11 int get_num(int a,int b,int c,int d)  12 {  13     int number = 0;  14     for(int i = 1; i <= 4; i++)  15  {  16         if((a & 1 << (i - 1)) == 0)    //位运算,1<<(i-1)=1*2^(i-1)

 17  {  18             number++;  19             num[1][i]++;  20             num[1][i - 1]++;  21             num[1][i + 1]++;  22             num[2][i]++;  23  }  24         if((b & 1 << (i - 1)) == 0)  25  {  26             number++;  27             num[2][i]++;  28             num[2][i - 1]++;  29             num[2][i + 1]++;  30             num[1][i]++;  31             num[3][i]++;  32  }  33         if((c & 1 << (i - 1)) == 0)  34  {  35             number++;  36             num[3][i]++;  37             num[3][i - 1]++;  38             num[3][i + 1]++;  39             num[2][i]++;  40             num[4][i]++;  41  }  42         if((d & 1 << (i - 1)) == 0)  43  {  44             number++;  45             num[4][i]++;  46             num[3][i]++;  47             num[4][i + 1]++;  48             num[4][i - 1]++;  49  }  50  }  51     return number;  52 }  53 

 54 void flip()  55 {  56     for(int i = 1; i <= 4; i++)  57         for(int j = 1; j <= 4; j++)  58             if(num[i][j] % 2 != 0)  59  {  60                 if(b[i][j] == 'w')  61                     b[i][j] = 'b';  62                 else

 63                     b[i][j] = 'w';  64  }  65 }  66 

 67 int checkb()  68 { 71     for(int i = 1; i <= 4; i++)  72         for(int j = 1; j <= 4; j++)  73             if(b[i][j] == 'w')  74                 return 0;  75     return 1;  76 }  77 

 78 int checkw()  79 { 82     for(int i = 1; i <= 4; i++)  83         for(int j = 1; j <= 4; j++)  84             if(b[i][j] == 'b')  85                 return 0;  86     return 1;  87 }  88 

 89 int main()  90 {  91     int ans = INF;  92     for(int i = 1; i <= 4; i++)  93         scanf("%s",a[i] + 1);  94     for(int i = 0; i < 16; i++)  95         for(int j = 0; j < 16; j++)  96             for(int k = 0; k < 16; k++)  97                for(int p = 0; p < 16; p++)  98  {  99                     memcpy(b,a,sizeof(a)); 100  mem0(num); 101                     int number = get_num(i,j,k,p); 102  flip(); 103                     if(checkb() || checkw()) 104                         ans = Min(ans,number); 105  } 106     if(ans == INF) 107         printf("Impossible\n"); 108     else

109     printf("%d\n",ans); 110     return 0; 111 }

 

B题和A题很像

大神的代码:

/*



参考高手的高效解法:

> 证明:要使一个为'+'的符号变为'-',必须其相应的行和列的操作数为奇数;可以证明,如果'+'位置对应的行和列上每一个位置都进行一次操作,则整个图只有这一'+'位置的符号改变,其余都不会改变.

> 设置一个4*4的整型数组,初值为零,用于记录每个点的操作数,那么在每个'+'上的行和列的的位置都加1,得到结果模2(因为一个点进行偶数次操作的效果和没进行操作一样,这就是楼上说的取反的原理),然后计算整型数组中一的

> 个数即为操作数,一的位置为要操作的位置(其他原来操作数为偶数的因为操作并不发生效果,因此不进行操作)

*********************************

此上证其可以按以上步骤使数组中值都为‘-’

********************************

在上述证明中将所有的行和列的位置都加1后,在将其模2之前,对给定的数组状态,将所有的位置操作其所存的操作数个次数,举例,如果a[i][j]==n,则对(i,j)操作n次,当所有的操作完后,即全为‘-’的数组。

其实就是不模2的操作,作了许多的无用功。

以上的操作次序对结果无影响,如果存在一个最小的步骤,则此步骤一定在以上操作之中。(简单说下:因为以上操作已经包含了所有可改变欲改变位置的操作了)

而模2后的操作是去掉了所有无用功之后的操作,此操作同样包含最小步骤。

但模2后的操作去掉任何一个或几个步骤后,都不可能再得到全为‘-’的。(此同样可证明:因为操作次序无影响,先进行最小步骤,得到全为‘-’,如果还剩下m步,则在全为‘-’的数组状态下进行这m步操作后还得到一个全为

‘-’的数组状态,此只能是在同一个位置进行偶数次操作,与前文模2后矛盾,所以m=0),因此模2后的操作即为最小步骤的操作。

*/

#include <iostream>

using namespace std;



bool mark[4][4];

char s[4][4];



int main()

{

    int i,j,k;

    int ci[16],cj[16];

    int nas = 0;

    memset(mark,0,sizeof(mark));

    for(i = 0;i < 4;i++)

        cin >> s[i];

    for(i = 0;i < 4;i++)

        for(j = 0;j < 4;j++)

        {

            char c = s[i][j];

            if(c == '+')

            {

                mark[i][j] = !mark[i][j];

                for(k = 0;k < 4;k++)

                {

                    mark[i][k] = !mark[i][k];

                    mark[k][j] = !mark[k][j];

                }

            }



        }

    for(i = 0;i < 4;i++)

        for(j = 0;j < 4;j++)

            if(mark[i][j] == true)

            {

                ci[nas] = i + 1;

                cj[nas] = j + 1;

                nas ++;

            }

    printf("%d\n",nas);

    for(i = 0;i < nas;i++)

    {

        printf("%d %d\n",ci[i],cj[i]);

    }

    return 0;

}

这个懂了,NB,在输入+-的时候略坑,上面的s[i]是一次输入一行,就不存在把回车当做一个字符了。时间为  94MS

这样输入更好:时间为32MS

for(i=0;i<3;i++)

        for(j=0;j<=4;j++)

         scanf("%c",&map2[i][j]);

    for(j=0;j<4;j++)

        scanf("%c",&map2[3][j]);
我的代码~~~我是取2的余数决定有没有翻:
 1 #include<iostream>

 2 #include<stdio.h>

 3 #include<string.h>

 4 using namespace std;

 5 int main()

 6 {

 7     int map[4][4],i,j,sum=0,k;

 8     char map2[4][4];

 9     memset(map,0,sizeof(map));

10     for(i=0;i<3;i++)

11         for(j=0;j<=4;j++)

12          scanf("%c",&map2[i][j]);

13     for(j=0;j<4;j++)

14         scanf("%c",&map2[3][j]);

15     getchar();

16     for(i=0;i<4;i++)

17         for(j=0;j<4;j++)

18         {

19             char c=map2[i][j];

20             if(c=='+')

21             {

22               map[i][j]++;

23                 for(k=0;k<4;k++)

24                 {

25                     map[i][k]++;

26                     map[k][j]++;

27                 }

28             }

29         }

30     for(i=0;i<4;i++)

31         for(j=0;j<4;j++)

32             if(map[i][j]%2==1)

33                 sum++;

34     printf("%d\n",sum);

35     for(i=0;i<4;i++)

36         for(j=0;j<4;j++)

37             if(map[i][j]%2==1)

38                 printf("%d %d\n",i+1,j+1);

39     return 0;

40 }
View Code
C题.....:
#include<iostream>

#include<string.h>

#include<stdio.h>

using namespace std;

int main()

{

    int n,i,a[500],b[500],maxn,map[100][100],j,k,q;

    while(~scanf("%d",&n)&&n)

    {

        memset(map,0,sizeof(map));

        int x,y,x1,y1;

        maxn=0;

        scanf("%d%d",&x,&y);

        for(i=0;i<n;i++)

        {

            scanf("%d%d",&a[i],&b[i]);

            map[a[i]][b[i]]=1;

        }

        scanf("%d%d",&x1,&y1);

        for(i=1;i<=x-x1+1;i++)

            for(j=1;j<=y-y1+1;j++)

            {

                q=0;

                for(k=0;k<n;k++)

                   if(a[k]>=i&&a[k]<i+x1&&b[k]>=j&&b[k]<j+y1&&map[a[k]][b[k]]==1)

                      q++;

                if(q>maxn)

                    maxn=q;

            }

        printf("%d\n",maxn);

    }

    return 0;

}

建立一个地图,有树的地方标记为1。再暴力就行了。

D题:表示还是不知道方法,但是要注意double 会失精度。

大神代码:

#include<stdio.h>

#include<stdlib.h>

#include<math.h>

int main()

{

    double a, b, c, A, B, C;

    int N, s, xo, yo, zo, X, Y, Z,XO,YO,ZO, re, sum;

    scanf("%d", &N);

    while(N--)

    {

        scanf("%d%lf%lf%lf", &s, &a,&b,&c);

        sum=s;

        A=b*c;

        B=a*c;

        C=a*b;

        xo=floor(s*A/(A+B+C));

        yo=floor(s*B/(A+B+C));

        zo=floor(s*C/(A+B+C));

        XO=xo-2>0?xo-2:0;

        YO=yo-2>0?yo-2:0;

        ZO=zo-2>0?zo-2:0;

        for(X=XO; X<=xo+2; X++)

        {

            for(Y=YO; Y<=yo+2; Y++)

            {

                for(Z=ZO;Z<=zo+2&&X+Y+Z<=s; Z++)

                {

                    re=floor(X*a);

                    if(floor(Y*b)<re)

                        re=floor(Y*b);

                    if(floor(Z*c)<re)

                        re=floor(Z*c);

                    re+=(s-X-Y-Z);

                    if(re>sum)

                        sum=re;

                }

            }

        }

        printf("%d\n", sum);

    }

    return 0;

}

真心不知道他是怎么分配的。。。

 另一种方法,注意精度。

 1 #include <algorithm>

 2 #include <stdio.h>

 3 #include<string.h>

 4 #define Max(a,b) (a > b ? a : b)

 5 int num[4];

 6 int main()

 7 {

 8     int n;

 9     int bb,bbb,cc,ccc,dd,ddd;

10     int d,b,c;

11     int a;

12     scanf("%d",&n);

13     while(n--)

14     {

15         memset(num,0,sizeof(num));

16         scanf("%d%d.%d%d.%d%d.%d",&a,&bb,&bbb,&cc,&ccc,&dd,&ddd);

17         b = (bb * 100 + bbb);

18         c = (cc * 100 + ccc);

19         d = (dd * 100 + ddd);

20         int ans = a;

21         for(int i=1;i<=a;i++)

22         {

23             if((num[1])*b<=(num[2])*c&&(num[1])*b<= (num[3])*d)   //一块钱一块钱放,放在收益最小的栏里

24                 num[1]++;

25             else if((num[2])*c<=(num[1])*b&&(num[2])*c<=(num[3])*d)

26                 num[2]++;

27             else  if((num[3])*d<=(num[2])*c&&(num[3])*d<=(num[1])* b)

28                  num[3]++;

29             if((num[1])*b<=(num[2])*c&&(num[1])*b<=(num[3])*d)

30                 ans= Max(ans,(num[1]*b)/100+(a-i));

31             else if((num[2])*c<=(num[1])*b&&(num[2])*c<=(num[3])*d)

32                 ans= Max(ans,(num[2]*c)/100+(a-i));

33             else  if((num[3])*d<=(num[2])*c&&(num[3])*d<=(num[1])*b)

34                 ans= Max(ans,(num[3]*d)/100+(a-i));     //最后和本金相比,取最大值

35         }

36         printf("%d\n",ans);

37     }

38     return 0;

39 }
View Code

F:

 1 #include <stdio.h>

 2 int main()

 3 {

 4    int a,b,c,d,e,num,n;

 5    while(~scanf("%d",&n))

 6    {

 7        num=0;

 8    for(a=0;a<=n;a++)

 9     for(b=0;b*5<=n-a;b++)

10     for(c=0;c*10<=n-a-5*b;c++)

11     for(d=0;d*25<=n-a-5*b-10*c;d++)

12      {

13          e=n-a-5*b-10*c-25*d;

14          if(e%50==0&&a+b+c+d+e/50<=100)

15          num++;

16      }

17     printf("%d\n",num);

18    }

19    return 0;

20 }
View Code

暴力也能过= =:109ms

你可能感兴趣的:(more)