第13届景驰-埃森哲杯广东工业大学ACM程序设计大赛部分题解
A 跳台阶
思路:其实很简单,不过当时直接dp来做了
AC代码:
#define _CRT_SECURE_NO_DEPRECATE #include<iostream> #include<cmath> #include<algorithm> #include<cstring> #include<vector> #include<string> #include<iomanip> #include<map> #include<stack> #include<set> #include<queue> #include<sstream> using namespace std; #define N_MAX 100000+2 #define INF 0x3f3f3f3f typedef long long ll; int n; int dp[31][31];//跳了i次,当前跳到台阶j int main() { int t; scanf("%d", &t); while (t--){ scanf("%d",&n); memset(dp,0,sizeof(dp)); for(int i=1;i<=n;i++) dp[1][i] = 1; for (int i = 2; i <= n;i++) { for (int j = 2; j <= n;j++) { for (int k = 1; k < j;k++) { dp[i][j] += dp[i - 1][k]; } } } int sum = 0; for (int i = 1; i <= n;i++) { sum += dp[i][n]; } printf("%d\n",sum); } return 0; }
D psd面试
题意:求一个字符串的最长回文子序列(注意这个回文子序列不一定是原字符串的连续子串)
思路:设原字符串为s1,s1反转后的字符串为s2,只要求s1,s2的最长公共子序列LCS即可。
AC代码:
#include<iostream> #include<vector> #include<set> #include<algorithm> #include<queue> #include<string> #include<cstring> using namespace std; #define N_MAX 1500+20 string s; int dp[N_MAX][N_MAX]; int main(){ while(cin>>s){ for(int i=0;i<s.size();i++) s[i]=tolower(s[i]); memset(dp,0,sizeof(dp)); string re_s(s); reverse(re_s.begin(),re_s.end()); int n=s.size(); for(int i=0;i<n;i++){ for(int j=0;j<n;j++){ if(s[i]==re_s[j]){ dp[i+1][j+1]=dp[i][j]+1; } else{ dp[i+1][j+1]=max(dp[i+1][j],dp[i][j+1]); } } } printf("%d\n",n-dp[n][n]); } return 0; }
E 回旋星空
题意:寻找回旋图标的个数,回旋图标定义:三个定点i,j,k,若d(i,j)=d(j,k),则i-j-k构成回旋图标,并且注意i-j-k和k-j-i不是一个回旋图标。
思路:回旋图标一定有中点,每次固定一个顶点,考虑这个顶点与其余的顶点的相连形成的线段当中是否长度相等的线段,若有,则以该顶点为中点,其中两条相等的线段为边,即可构成两个回旋图标。所以每次只要先固定一个顶点,从该点出发寻找是否存在长度相等的线段,并分别计算回旋图标个数即可。
AC代码:
#define _CRT_SECURE_NO_DEPRECATE #include<iostream> #include<vector> #include<set> #include<algorithm> #include<queue> #include<string> #include<cstring> #include<map> using namespace std; #define N_MAX 1000+2 typedef long long ll; struct point{ int x,y; point(int x=0,int y=0):x(x),y(y){} }p[N_MAX]; int d[N_MAX][N_MAX]; int main() { int t;scanf("%d",&t); while(t--){ memset(d,0,sizeof(d)); int n;scanf("%d",&n); for(int i=0;i<n;i++){ scanf("%d%d",&p[i].x,&p[i].y); } for(int i=0;i<n;i++){ for(int j=i+1;j<n;j++){ d[i][j]=d[j][i]=(p[i].x-p[j].x)*(p[i].x-p[j].x)+(p[i].y-p[j].y)*(p[i].y-p[j].y); } } for(int i=0;i<n;i++) sort(d[i],d[i]+n); int sum=0; for(int i=0;i<n;i++){//以每一个点为中转站 int num=1;//相连的边的条数 for(int j=1;j<n;j++){ if(d[i][j]==d[i][j-1]){ num++; } else{ sum+=num*(num-1); num=1; } } sum+=num*(num-1); } if(sum==0)printf("WA\n"); else printf("%d\n",sum); } return 0; }
F 等式
题意:给定n,求1/x + 1/y = 1/n (x<=y)的解数。(x、y、n均为正整数)
思路:等式转化成 nx+ny-xy=0 (等式左右两边分别加上n^2并化简)==>(x-n)(y-n)=n^2,所以只要找到n^2的所有因子即可
AC代码:
#define _CRT_SECURE_NO_DEPRECATE #include<iostream> #include<vector> #include<set> #include<algorithm> #include<queue> #include<string> #include<cstring> #include<map> using namespace std; #define N_MAX 100000+2 typedef long long ll; int n; map<int,int>prime(int n){ map<int,int>m; for(int i=2;i*i<=n;i++){ while(n%i==0){ m[i]++; n/=i; } } if(n!=1)m[n]++; return m; } int main() { int t;cin>>t; while(t--){ cin>>n; map<int,int>mp=prime(n); int sum=1; for(map<int,int>::iterator it=mp.begin();it!=mp.end();it++){ sum*=(it->second*2+1); } printf("%d\n",(sum+1)/2); return 0; }
J 强迫症的序列
题意:有n个数,每次除了最大的那个数以外其余的都加1,直到所有的数大小相等为止,输出操作次数以及最终的值。
思路:为了求操作次数,换一个方向考虑,题意说除了最大的数字之外其余的数加1,这等价于让当前最大的数字减1,其余数字数值保持不变。这样一来直到所有的数字都和最小的那个数字一样大时不需要再进行操作了。减1的次数即是操作数。
#include<iostream> #include<vector> #include<set> #include<algorithm> #include<queue> #include<string> using namespace std; #define N_MAX 100000+20 int n,a[N_MAX]; int main(){ int t;cin>>t; while(t--){ cin>>n; for(int i=0;i<n;i++)cin>>a[i]; sort(a,a+n); int sum=0; for(int i=1;i<n;i++){ sum+=a[i]-a[0]; } cout<<sum<<" "<<sum+a[0]<<endl; } return 0; }
G 旋转矩阵
题意:对一个n*m的字符串矩阵进行顺时针逆时针的旋转操作,打印旋转后的矩阵,并注意'|'旋转后后会变成'-','-'会变成'|'
思路:模拟旋转操作即可,设旋转后的矩阵为cur,旋转前的矩阵为prev,找到这两个矩阵元素之间的对应关系即可,若是prev向左旋转得到cur,则对应关系为:cur[m-j+1][i]=prev[i][j](若prev[i][j]是'|'或'-',则旋转后两者互相变换为对方),若prev向右旋转变换到cur,变换关系为cur[j][n-i+1]=prev[i][j](同上)。
AC代码
#define _CRT_SECURE_NO_DEPRECATE #include<iostream> #include<vector> #include<set> #include<algorithm> #include<queue> #include<string> #include<cstring> using namespace std; #define N_MAX 30+20 int n, m; char pic[N_MAX][N_MAX]; string ope; int num_l = 0, num_r = 0; void ope_L(char pic[N_MAX][N_MAX], int n, int m) {//左旋转 char tmp[N_MAX][N_MAX]; memcpy(tmp, pic, sizeof(tmp)); for (int i = 1; i<=n; i++) { for (int j = 1; j<=m; j++) { if (tmp[i][j] == '|')pic[m - j + 1][i] = '-'; else if (tmp[i][j] == '-')pic[m - j + 1][i] = '|'; else pic[m - j + 1][i] = tmp[i][j]; } } } void ope_R(char pic[N_MAX][N_MAX],int n,int m) { char tmp[N_MAX][N_MAX]; memcpy(tmp, pic, sizeof(tmp)); for (int i = 1; i <= n;i++) { for (int j = 1; j <= m; j++) { if (tmp[i][j] == '|')pic[j][n - i + 1] = '-'; else if (tmp[i][j] == '-')pic[j][n - i + 1] = '|'; else pic[j][n - i + 1] = tmp[i][j]; } } } int main() { int t; scanf("%d", &t); while (t--) { memset(pic,0,sizeof(pic)); scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { scanf(" %c", &pic[i][j]); } } cin >> ope; num_l = num_r = 0; for (int i = 0; i < ope.size(); i++) { if (ope[i] == 'L')num_l++; else num_r++; } if (num_l > num_r) { num_l -= num_r; num_r = 0; num_l %= 4; } else { num_r -= num_l; num_l = 0; num_r %= 4; } while (num_l--) { ope_L(pic, n, m); swap(n, m); } while (num_r--) { ope_R(pic, n, m); swap(n, m); } printf("%d %d\n", n, m); for (int i = 1; i <= n;i++) { for (int j = 1; j <= m;j++) { printf("%c",pic[i][j]); } cout << endl; } cout << endl; } return 0; }
K 密码
题意:字符串Z字排列后再一行一行从左到右的输出。
思路:找规律 第i行字符之间间隔数d1=2(n-1)-2(i-1),d2=2(n-1)-d1;(i=1,2,...,n),间隔d1,d2交替使用
譬如第一行,字符间隔d1=2(n-1),d2为0;....
按照间隔得到新的字符串排列。
AC代码:
#define _CRT_SECURE_NO_DEPRECATE #include<iostream> #include<vector> #include<set> #include<algorithm> #include<queue> #include<string> #include<cstring> using namespace std; #define N_MAX 100000+2 typedef long long ll; int n; string s; char fina[N_MAX]; int main() { int t;cin>>t; while(t--){ scanf("%d",&n);cin>>s; if(s.size()<=n||n==1){cout<<s<<endl;continue;} int num=0;//记录字符串输出顺序 int x=2*n-2; for(int i=1;i<=n;i++){ int d1=x-2*(i-1);int d2=x-d1; int k=i-1,cnt=0; while(k<s.size()){///!!! fina[num++]=s[k]; if(d1==0)k+=d2; else if(d2==0)k+=d1; else{ if(!(cnt&1))k+=d1; else k+=d2; cnt++; } } } for(int i=0;i<num;i++) printf("%c",fina[i]); puts(""); } return 0; }
L 用来作弊的药水
直接用快速幂运算
AC代码:
#define _CRT_SECURE_NO_DEPRECATE #include<iostream> #include<cmath> #include<algorithm> #include<cstring> #include<vector> #include<string> #include<iomanip> #include<map> #include<stack> #include<set> #include<queue> #include<sstream> using namespace std; #define N_MAX 100000+2 #define INF 0x3f3f3f3f #define MOD 1000000005 typedef long long ll; int n; ll mod_pow(ll x,ll n) { ll res = 1; while (n) { if(n&1)res = res*x % MOD; x = x*x % MOD; n >>= 1; } return res; } int main() { int t; scanf("%d", &t); while (t--){ ll x, y, a, b; scanf("%lld%lld%lld%lld",&x,&a,&y,&b); ll x1 = mod_pow(x, a); ll x2 = mod_pow(y,b); if (x1 == x2)puts("Yes"); else puts("No"); } return 0; }