一、Wlacm
1.一笔画问题
题意:
如果一个图存在一笔画,则一笔画的路径叫做欧拉路,如果最后又回到起点,那这个路径叫做欧拉回路。
我们定义奇点是指跟这个点相连的边数目有奇数个的点。对于能够一笔画的图,我们有以下两个定理。
定理1:存在欧拉路的条件:图是连通的,有且只有2个奇点。
定理2:存在欧拉回路的条件:图是连通的,有0个奇点。
两个定理的正确性是显而易见的,既然每条边都要经过一次,那么对于欧拉路,除了起点和终点外,每个点如果进入了一次,显然一定要出去一次,显然是偶点。对于欧拉回路,每个点进入和出去次数一定都是相等的,显然没有奇点。
求欧拉路的算法很简单,使用深度优先遍历即可。
根据一笔画的两个定理,如果寻找欧拉回路,对任意一个点执行深度优先遍历;找欧拉路,则对一个奇点执行DFS,时间复杂度为O(m+n),m为边数,n是点数。
以下是寻找一个图的欧拉路的算法实现
输入:
输入有多行,第一行n,m,有n个点,m条边,以下m行描述每条边连接的两点。
已知 1≤n≤25,1≤m≤25
输出:
输出欧拉路或欧拉回路
样例输入:
5 5
1 2
2 3
3 4
4 5
5 1
样例输出:
1 5 4 3 2 1
解题思路:
就是用二维数组记录两个点,然后找到奇点,进行dfs找到输出即可
程序代码:
#include<bits/stdc++.h>
using namespace std;
int n,m;
int c=0;
int num[50][50];
int ans[100];
int s[25];
void dfs(int i){
for(int j=1;j<=n;j++){
if(num[i][j]==1){
num[i][j]=num[j][i]=0;
dfs(j);
}
}
ans[++c]=i;
}
int main(){
memset(num,0,sizeof(num));
cin>>n>>m;
for(int i=1;i<=m;i++){
int a,b;
cin>>a>>b;
num[a][b]=num[b][a]=1;
s[a]++;
s[b]++;
}
int flag=1;
for(int i=1;i<=n;i++){
if(s[i]%2==1)
flag=i;
}
dfs(flag);
for(int i=1;i<=c;i++)
cout<<ans[i]<<" ";
cout<<endl;
return 0;
}
2.骑马修栅栏
题意:
农民John每年有很多栅栏要修理。他总是骑着马穿过每一个栅栏并修复它破损的地方。
John是一个与其他农民一样懒的人。他讨厌骑马,因此从来不两次经过一个一个栅栏。你必须编一个程序,读入栅栏网络的描述,并计算出一条修栅栏的路径,使每个栅栏都恰好被经过一次。John能从任何一个顶点(即两个栅栏的交点)开始骑马,在任意一个顶点结束。
每一个栅栏连接两个顶点,顶点用1到500标号(虽然有的农场并没有500个顶点)。一个顶点上可连接任意多(>=1)个栅栏。所有栅栏都是连通的(也就是你可以从任意一个栅栏到达另外的所有栅栏)。
你的程序必须输出骑马的路径(用路上依次经过的顶点号码表示)。我们如果把输出的路径看成是一个500进制的数,那么当存在多组解的情况下,输出500进制表示法中最小的一个 (也就是输出第一个数较小的,如果还有多组解,输出第二个数较小的,等等)。 输入数据保证至少有一个解。
输入:
第1行: 一个整数F(1 <= F <= 1024),表示栅栏的数目
第2到F+1行: 每行两个整数i, j(1 <= i,j <= 500)表示这条栅栏连接i与j号顶点
输出:
输出应当有F+1行,每行一个整数,依次表示路径经过的顶点号。注意数据可能有多组解,但是只有上面题目要求的那一组解是认为正确的。
样例输入:
9
1 2
2 3
3 4
4 2
4 5
2 5
5 6
5 7
4 6
样例输出:
1
2
3
4
2
5
4
6
5
7
程序代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1024+5;
const int M=0x3f3f3f3f;
int mi=M,mn=-M;
int c=0;
int num[N][N],s[N],ans[N];
int n;
void dfs(int i){
for(int j=mi;j<=mn;j++){
if(num[i][j]>0){
num[i][j]--;
num[j][i]--;
dfs(j);
}
}
ans[++c]=i;
}
int main(){
memset(num,0,sizeof(num));
scanf("%d",&n);
for(int i=1;i<=n;i++){
int a,b;
scanf("%d %d",&a,&b);
num[a][b]++;
num[b][a]++;
s[a]++;
s[b]++;
mi=min(a,min(b,mi));
mn=max(a,max(b,mn));
}
int flag=mi;
for(int i=mi;i<=mn;i++){
if(s[i]%2==1){
flag=i;
break;
}
}
dfs(flag);
for(int i=c;i>=1;i--)
printf("%d\n",ans[i]);
return 0;
}
3.珍珠BEAD
题意:
有n颗形状和大小都一致的珍珠,它们的重量都不相同。n为整数,所有的珍珠从1到n编号。你的任务是发现哪颗珍珠的重量刚好处于正中间,即在所有珍珠的重量中,该珍珠的重量列(n+1)/2位。下面给出将一对珍珠进行比较的办法:
给你一架天平用来比较珍珠的重量,我们可以比出两个珍珠哪个更重一些,在作出一系列的比较后,我们可以将某些肯定不具备中间重量的珍珠拿走。
例如,下列给出对5颗珍珠进行四次比较的情况:
1、珍珠2比珍珠1重 2、珍珠4比珍珠3重 3、珍珠5比珍珠1重 4、珍珠4比珍珠2重
根据以上结果,虽然我们不能精确地找出哪个珍珠具有中间重量,但我们可以肯定珍珠1和珍珠4不可能具有中间重量,因为珍珠2、4、5比珍珠1重,而珍珠1、2、3比珍珠4轻,所以我们可以移走这两颗珍珠。
写一个程序统计出共有多少颗珍珠肯定不会是中间重量。
输入:
输入文件第一行包含两个用空格隔开的整数N和M,其中1<=N<=99,且N为奇数,M表示对珍珠进行的比较次数,接下来的M行每行包含两个用空格隔开的整数x和y,表示珍珠x比珍珠y重。
输出:
输出文件仅一行包含一个整数,表示不可能是中间重量的珍珠的总数。
样例输入:
5 4
2 1
4 3
5 1
4 2
样例输出:
2
解体思路:
只要有一半的数大于或小于它,它就不可能了
程序代码:
#include<bits/stdc++.h>
using namespace std;
const int N=100+30;
int a[N][N],b[N][N],num1[N],num2[N];
int main()
{
int n,m,x,y;
cin>>n>>m;
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
memset(num1,0,sizeof(num1));
memset(num2,0,sizeof(num2));
for(int i=1;i<=m;i++)
{
cin>>x>>y;
a[x][y]=1;//x比y重
b[y][x]=1;//y比x轻
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
for(int k=1;k<=n;k++)
{
if(i==j||i==k||j==k)continue;
if(a[i][k]&&a[k][j])//若i比k重且k比j重
{
a[i][j]=1;//则i比j重
b[j][i]=1;//则j比i轻
}
if(b[i][k]&&b[k][j])//若i比k重且k比j轻
{
b[i][j]=1;//则i比j轻
a[j][i]=1;//则j比i重
}
}
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(i==j)continue;
if(a[i][j])num1[i]++;//统计比i重的个数
if(b[i][j])num2[i]++;//统计比i轻的个数
}
}
int sum=0;
for(int i=1;i<=n;i++)
{
if(num1[i]>=(n+1)/2||num2[i]>=(n+1)/2)sum++;//枚举符合条件的点
}
cout<<sum<<endl;
return 0;
}
4.铲雪车snow
题意:
随着白天越来越短夜晚越来越长,我们不得不考虑铲雪问题了。整个城市所有的道路都是双车道,因为城市预算的削减,整个城市只有1辆铲雪车。铲雪车只能把它开过的地方(车道)的雪铲干净,无论哪儿有雪,铲雪车都得从停放的地方出发,游历整个城市的街道。现在的问题是:最少要花多少时间去铲掉所有道路上的雪呢?
输入:
输入数据的第1行表示铲雪车的停放坐标(x,y),x,y为整数,单位为米。下面最多有100行,每行给出了一条街道的起点坐标和终点坐标,所有街道都是笔直的,且都是双向一个车道。铲雪车可以在任意交叉口、或任何街道的末尾任意转向,包括转U型弯。铲雪车铲雪时前进速度为20 km/h,不铲雪时前进速度为50 km/h。
保证:铲雪车从起点一定可以到达任何街道。
输出:
铲掉所有街道上的雪并且返回出发点的最短时间,精确到分种。
样例输入:
0 0
0 0 10000 10000
5000 -10000 5000 10000
5000 10000 10000 10000
样例输出:
3:55
3小时55分钟
程序代码:
#include<bits/stdc++.h>
using namespace std;
int main(){
// int T;
// scanf("%d",&T);
// while(T--){
long long x,y;
scanf("%lld %lld",&x,&y);
double dis=0;
long long x1,x2,y1,y2;
while(~scanf("%lld %lld %lld %lld",&x1,&y1,&x2,&y2)){
dis += sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
}
double ans=dis*2/1000/20;
long long h=(int)(ans);
long long m=(int)((ans-h)*60+0.5);
printf("%d:%02d\n",h,m);
// }
return 0;
}
二、PTA
1.打印沙漏
题意:
本题要求你写个程序把给定的符号打印成沙漏的形状。例如给定17个“*”,要求按下列格式打印
*****
***
*
***
*****
所谓“沙漏形状”,是指每行输出奇数个符号;各行符号中心对齐;相邻两行符号数差2;符号数先从大到小顺序递减到1,再从小到大顺序递增;首尾符号数相等。
给定任意N个符号,不一定能正好组成一个沙漏。要求打印出的沙漏能用掉尽可能多的符号。
输入:
输入在一行给出1个正整数N(≤1000)和一个符号,中间以空格分隔。
输出:
首先打印出由给定符号组成的最大的沙漏形状,最后在一行中输出剩下没用掉的符号数。
样例输入:
19 *
样例输出:
*****
***
*
***
*****
2
解题思路:
其实没什么难的,就是要注意打印空格和字符的区间即可。
程序代码:
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;
char a;
cin>>n>>a;
int num=1; //记录一共要用几个字符
int ceng=0; //上半层数
int cnt=1; //每行的个数
while(num<=n){
ceng++;
cnt+=2;
num+=(cnt*2);
}
num=num-cnt*2;
cnt=cnt-2;
for(int i=1;i<=ceng;i++){
int k=i-1;
for(int j=1;j<=k;j++)
cout<<" ";
for(int j=k+1;j<=cnt/2+1;j++)
cout<<a;
for(int j=1;j<=cnt/2-k;j++)
cout<<a;
cout<<endl;
}
for(int i=ceng-1;i>=1;i--){
int k=i-1;
for(int j=1;j<=k;j++)
cout<<" ";
for(int j=k+1;j<=cnt/2+1;j++)
cout<<a;
for(int j=1;j<=cnt/2-k;j++)
cout<<a;
cout<<endl;
}
cout<<n-num<<endl;
return 0;
}
2.素数对猜想
题意:
让我们定义dn 为:dn =pn+1 −pn ,其中pi 是第i个素数。显然有d1 =1,且对于n>1有dn 是偶数。“素数对猜想”认为“存在无穷多对相邻且差为2的素数”。
现给定任意正整数N(<10^5 ),请计算不超过N的满足猜想的素数对的个数。
输入:
输入在一行给出正整数N。
输出:
在一行中输出不超过N的满足猜想的素数对的个数。
样例输入:
20
样例输出:
4
程序代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5;
int a[N+5]={0,1,2,3};
void solve(){
int cnt=4;
for(int i=4;i<=N;i++){
int t=i;
int flag=1;
for(int j=2;j<=sqrt(t);j++){
if(t%j==0){
flag=0;
break;
}
}
if(flag)
a[cnt++]=t;
}
}
int main(){
solve();
// for(int i=0;i<=10;i++)
// cout<<a[i]<<endl;
int n;
cin>>n;
int cnt=0;
for(int i=2;i<=n;i++){
if(a[i]-a[i-1]==2 && a[i]<=n)
cnt++;
}
cout<<cnt<<endl;
return 0;
}
3.数组元素循环右移问题
题意:
一个数组A中存有N(>0)个整数,在不允许使用另外数组的前提下,将每个整数循环向右移M(≥0)个位置,即将A中的数据由(A0 A1 ⋯AN−1 )变换为(AN−M ⋯AN−1 A0 A1 ⋯AN−M−1 )(最后M个数循环移至最前面的M个位置)。如果需要考虑程序移动数据的次数尽量少,要如何设计移动的方法?
输入:
每个输入包含一个测试用例,第1行输入N(1≤N≤100)和M(≥0);第2行输入N个整数,之间用空格分隔。
输出:
在一行中输出循环右移M位以后的整数序列,之间用空格分隔,序列结尾不能有多余空格。
样例输入:
6 2
1 2 3 4 5 6
样例输出:
5 6 1 2 3 4
解题思路:
题目不让开新数组,我就把原来的数组开大一点,先把所有的后移,然后再把后面的移到前面来。
程序代码:
#include<bits/stdc++.h>
using namespace std;
int a[200+10];
int main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>a[i];
if(m<n||m>n){
m=m%n;
for(int i=n;i>=1;i--){
a[i+m]=a[i];
}
int cnt=1;
for(int i=n+1;i<=n+m;i++)
a[cnt++]=a[i];
for(int i=1;i<=n;i++){
if(i==1)
cout<<a[i];
else
cout<<" "<<a[i];
}
cout<<endl;
}else if(m%n==0){
for(int i=1;i<=n;i++){
if(i==1)
cout<<a[i];
else
cout<<" "<<a[i];
}
cout<<endl;
}
return 0;
}
程序代码:(网上找了一个正确的代码,没有用新的数组,太聪明啦!)
#include <stdio.h>
int main()
{
int n,m;
scanf("%d %d",&n,&m);
if(n<=100&&n>=1){
int temp[n];
for(int i=0;i<n;i++){
m=m%n;
scanf("%d",&temp[m]);
m++;
}
int count=1;
for(int i=0;i<n;i++){
printf("%d",temp[i]);
if(count<n){
printf(" ");
count++;
}
}
}
}
4.Have Fun with Numbers
题意:
Notice that the number 123456789 is a 9-digit number consisting exactly the numbers from 1 to 9, with no duplication. Double it we will obtain 246913578, which happens to be another 9-digit number consisting exactly the numbers from 1 to 9, only in a different permutation. Check to see the result if we double it again!
Now you are suppose to check if there are more numbers with this property. That is, double a given number with k digits, you are to tell if the resulting number consists of only a permutation of the digits in the original number.
输入:
Each input contains one test case. Each case contains one positive integer with no more than 20 digits.
输出:
For each test case, first print in a line “Yes” if doubling the input number gives a number that consists of only a permutation of the digits in the original number, or “No” if not. Then in the next line, print the doubled number.
样例输入:
1234567899
样例输出:
Yes
2469135798
解题思路:
因为最多有20位,所以要用到大数乘法,然后比较乘2后每位数出现的次数,如果和之前的一样就输出Yes,否则输出No。但是无论是对还是错,都要输出结果,刚开始我只在输出Yes的时候输出结果,结果一直过不了,后来才知道。
程序代码:
#include<bits/stdc++.h>
using namespace std;
int a[10],b[10],ans[25];
void muli()
{
int i;
for(i=1;i<=ans[0];i++)
ans[i]=ans[i]*2;//先每位乘起来
for(i=1;i<=ans[0];i++) //处理进位
{
ans[i+1]+=ans[i]/10;
ans[i]%=10;
}
while(ans[ans[0]+1]>0)//处理最高位相乘的进位
{
ans[0]++;
ans[ans[0]+1]=ans[ans[0]]/10;
ans[ans[0]]=ans[ans[0]]%10;
}
}
bool cmp(){
for(int i=1;i<=ans[0];i++){
b[ans[i]]++;
}
for(int i=0;i<10;i++){
if(a[i]!=b[i])
return false;
}
return true;
}
int main(){
string s;
cin>>s;
int temp=s.length();
ans[0]=s.length();
int cnt=1;
for(int i=ans[0]-1;i>=0;i--){
ans[cnt++]=s[i]-'0';
a[s[i]-'0']++;
}
muli();
// if(ans[0]!=temp){
// cout<<"No"<<endl;
// for(int i=ans[0];i>=1;i--)
// cout<<ans[i];
// cout<<endl;
// return 0;
// }
if(cmp()){
cout<<"Yes"<<endl;
for(int i=ans[0];i>=1;i--)
cout<<ans[i];
cout<<endl;
}else{
cout<<"No"<<endl;
for(int i=ans[0];i>=1;i--)
cout<<ans[i];
cout<<endl;
}
return 0;
}
5.Shuffling Machine
题意:
Shuffling is a procedure used to randomize a deck of playing cards. Because standard shuffling techniques are seen as weak, and in order to avoid “inside jobs” where employees collaborate with gamblers by performing inadequate shuffles, many casinos employ automatic shuffling machines. Your task is to simulate a shuffling machine.
The machine shuffles a deck of 54 cards according to a given random order and repeats for a given number of times. It is assumed that the initial status of a card deck is in the following order:
S1, S2, ..., S13,
H1, H2, ..., H13,
C1, C2, ..., C13,
D1, D2, ..., D13,
J1, J2
where “S” stands for “Spade”, “H” for “Heart”, “C” for “Club”, “D” for “Diamond”, and “J” for “Joker”. A given order is a permutation of distinct integers in [1, 54]. If the number at the i-th position is j, it means to move the card from position i to position j. For example, suppose we only have 5 cards: S3, H5, C1, D13 and J2. Given a shuffling order {4, 2, 5, 3, 1}, the result will be: J2, H5, D13, S3, C1. If we are to repeat the shuffling again, the result will be: C1, H5, S3, J2, D13.
输入:
Each input file contains one test case. For each case, the first line contains a positive integer K (≤20) which is the number of repeat times. Then the next line contains the given order. All the numbers in a line are separated by a space.
输出:
For each test case, print the shuffling results in one line. All the cards are separated by a space, and there must be no extra space at the end of the line.
样例输入:
2
36 52 37 38 3 39 40 53 54 41 11 12 13 42 43 44 2 4 23 24 25 26 27 6 7 8 48 49 50 51 9 10 14 15 16 5 17 18 19 1 20 21 22 28 29 30 31 32 33 34 35 45 46 47
样例输出:
S7 C11 C10 C12 S1 H7 H8 H9 D8 D9 S11 S12 S13 D10 D11 D12 S3 S4 S6 S10 H1 H2 C13 D2 D3 D4 H6 H3 D13 J1 J2 C1 C2 C3 C4 D1 S5 H5 H11 H12 C6 C7 C8 C9 S2 S8 S9 H10 D5 D6 D7 H4 H13 C5
程序代码:
#include<bits/stdc++.h>
using namespace std;
void my_sort(int ar[],int br[],int cr[]){
for(int i=1;i<55;i++){
cr[br[i]]=ar[i];
}
}
void print(int ar[]){
for (int i = 1; i < 55; i++)
{
if (ar[i] >= 1 && ar[i]<=13)
cout << "S"<<ar[i] ;
else if (ar[i] >= 14 && ar[i] <= 26)
cout << "H" << ar[i]-13 ;
else if (ar[i] >= 27 && ar[i] <= 39)
cout << "C" << ar[i]-26 ;
else if (ar[i] >= 40 && ar[i] <= 52)
cout << "D" << ar[i]-39 ;
else if (ar[i] == 53)
cout << "J1";
else
cout << "J2";
if (i != 54)
cout << " ";
}
}
int main(){
int ar[55],br[55],temp[55];
int flag=0;
for(int i=1;i<=54;i++)
ar[i]=i;
int n;
cin>>n;
for(int i=1;i<=54;i++)
cin>>br[i];
while(n--){
if(flag==0){
my_sort(ar,br,temp);
flag=1;
}else{
my_sort(temp,br,ar);
flag=0;
}
}
if(flag==0)
print(ar);
else
print(temp);
return 0;
}