Q1(uva 725):
给出一个整数n,找到所有的0~9的排列,是的前五个数组成的整数能够整除后五个数组成的整数。
分析:很典型的基本暴力枚举法,暴力求解往往伴随优化。这道题目的优化点在于枚举后五位得到10x9x8x7x6种情况,然后基于这些情况和等式关系,然后得到前面的整数,然后只需判断一下是否满足每个数字只出现了一次即可,由于题目是要求从小到大输出,这里枚举的时候控制一下从小到大枚举即可。
参考代码如下:
#include<cstdio> #include<cstring> using namespace std; int main() { int i , j , k , m , n; int visit[10]; int x; int cas = 0; while(scanf("%d",&x) && x) { if(cas++) {printf(" ");} int ok = 0; int flag = 0; for(int i = 0;i <= 9;i++) { for(j = 0;j <= 9;j++) { if(i == j) continue; for(k = 0;k <= 9;k++) { if(k == i || k == j) continue; for(m = 0;m <= 9;m++) { if(m == i || m == j || m == k) continue; for(n = 0;n <= 9;n++) { memset(visit , 0 , sizeof(visit)); if(n == i || n ==j || n == k || n == m) continue; int sum = 10000*i + 1000*j + 100*k + 10*m +n; if(x*sum >= 99999){flag = 1;break;} visit[i] = 1;visit[j] = 1;visit[k] = 1;visit[m] = 1;visit[n] = 1; int temp = x*sum; int nn = temp%10;temp/= 10; int mm = temp%10;temp/= 10; int kk = temp%10;temp/= 10; int jj = temp%10;temp/= 10; int ii = temp%10; visit[ii] = 1;visit[jj] = 1;visit[kk] = 1;visit[mm] = 1;visit[nn] = 1; int cnt = 0; for(int p = 0;p <= 9;p++) if(visit[p]) cnt++; if(cnt == 10) {printf("%d%d%d%d%d / %d%d%d%d%d = %d ",ii,jj,kk,mm,nn,i,j,k,m,n,x);ok=1;} } if(flag) break; } if(flag) break; } if(flag) break; } if(flag) break; } if(!ok) printf("There are no solutions for %d. ",x); } }
Q2(hdu 1422):
顺序给出1,2,3…n个城市的生活费和花费,他们组成一个环123…n1,现在你只能顺时针游览,请问你最多游览多少个城市?
分析:这道题目本质上还是暴力,但是伪装了一层优化的分析,容易想到枚举开始的城市,然后遍历n个位置维护一个最大值即可,但是这样是个O(n^2)的时间复杂度,考察这题的数据量n最大是100000,显然这个方法会超时。
进一步思考,假设我们当前的状态是从i出发,走到了城市j,在游览j-1城市的时候还没有被赶回家,游览第j个城市被赶回去了,按照我们上面枚举的方法,下次的状态是从i-1出发。但是其中的技巧在于,第一种状态如果存在,i城市的净收入一定为正,因此从i-1城市出发,到达j城市的净收入必然为负,还是要被赶回家,因此下面要枚举的量应该是第j+1个城市,之前那个状态游览的城市为j-i+1,将其存起来,维护一个最大值即可。
参考代码如下:
#include<cstdio> #include<algorithm> using namespace std; const int maxn = 200000 + 5; int a[maxn]; int main() { int n; while(scanf("%d",&n) != EOF) { for(int i = 1;i <= n;i++) { int x , y; scanf("%d%d",&x,&y); a[i] = x - y; a[n+i] = x - y; } //for(int i = 1;i <= n;i++) printf("%d ",a[i]); int num = 0; int Max = 0; int ans = 0; for(int i = 1;i <= 2*n;i++) { ans += a[i]; if(ans >= 0) num++; else { Max = max(num , Max); //printf("%d ",Max); num = 0 , ans = 0; } Max = max(num , Max); if(Max == n) break; } printf("%d ",Max); } }
Q3(最大乘积):
给出n个元素组成的序列(n∈[1,18]),每个元素的绝对值大小不超过10,找到一个连续子序列,是的子序列各元素的乘积是最大的,输出这个最大值,如果最大值为负数,那么认为0是问题的答案。
分析:连续子序列有两个要素,即起点和终点,记为有序对(x,y),考察数据大小,数据量较小,因此直接枚举即可维护最大值即可,这里记录最大值的变量最大可以达到10^18,因此用long long储存。
#include<cstdio> #include<algorithm> using namespace std; const int maxn = 20; int main() { int a[maxn]; int n; int cas = 1; while(scanf("%d",&n) != EOF) { long long Max = 0; int ok = 0; for(int i = 0;i < n;i++) scanf("%d",&a[i]); for(int i = 0;i < n;i++) for(int j = i;j < n;j++) { long long num = 1; for(int k = i;k <=j;k++) {num *= a[k];} // printf("%d ",num); if(num <= 0) continue; else Max = max(Max , num); // printf("%d%d ",i , j); } printf("Case #%d: The maximum product is %lld. ",cas++,Max); } }
Q4(uva10976):
输入正整数k,找到所有的整数对(x,y),满足x>y,且1/k = 1/x + 1/y.
分析:这个题的点在于分分析出枚举对象的范围,等式有2个变元,我们枚举一个,容易利用等式关系求得另一个。这里利用不等关系x>y,容易推得1/k≤2/y,这样我们看到y的范围是[1,2k]。
参考代码如下:
#include<cstdio> using namespace std; int main() { int k; while(scanf("%d",&k) != EOF) { int cnt = 0; for(int y = 1;y <= 2*k;y++) { if(y - k <= 0) continue; if((k*y%(y-k)) == 0) cnt++; } printf("%d ",cnt); for(int y = 1;y <= 2*k;y++) { if(y - k <= 0) continue; if((k*y%(y-k)) == 0) {printf("1/%d = 1/%d + 1/%d ",k , k*y/(y-k) , y);} } } }