第一次训练 密码: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
");
108     else
109     printf("%d
",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
");
    else
      printf("%d
",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
",step);
    else 
        printf("Impossible
");
    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
");
        else printf("%d
",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
");
108     else
109     printf("%d
",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
",nas);
    for(i = 0;i < nas;i++)
    {
        printf("%d %d
",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
",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
",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
",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
", 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
",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
",num);
18    }
19    return 0;
20 }
View Code

暴力也能过= =:109ms

原文地址:https://www.cnblogs.com/riddle/p/3203415.html