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 }
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 }
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 }
暴力也能过= =:109ms