惹某人持续划水的开学第二周(习题+感悟)

(一)图论

A - 一笔画问题

题目描述

    如果一个图存在一笔画,则一笔画的路径叫做欧拉路,如果最后又回到起点,那这个路径叫做欧拉回路。
    我们定义奇点是指跟这个点相连的边数目有奇数个的点。对于能够一笔画的图,我们有以下两个定理。
    定理1:存在欧拉路的条件:图是连通的,有且只有2个奇点。
    定理2:存在欧拉回路的条件:图是连通的,有0个奇点。
    两个定理的正确性是显而易见的,既然每条边都要经过一次,那么对于欧拉路,除了起点和终点外,每个点如果进入了一次,显然一定要出去一次,显然是偶点。对于欧拉回路,每个点进入和出去次数一定都是相等的,显然没有奇点。
    求欧拉路的算法很简单,使用深度优先遍历即可。
    根据一笔画的两个定理,如果寻找欧拉回路,对任意一个点执行深度优先遍历;找欧拉路,则对一个奇点执行DFS,时间复杂度为O(m+n),m为边数,n是点数。
    以下是寻找一个图的欧拉路的算法实现

Input

输入有多行,第一行n,m,有n个点,m条边,以下m行描述每条边连接的两点。
已知 1≤n≤25,1≤m≤25

Output

输出欧拉路或欧拉回路

Sample Input

5 5
1 2
2 3
3 4
4 5
5 1

Sample Output

1 5 4 3 2 1

理解

题目关于欧拉路和欧拉回路的知识点已经分析的很透彻了,无外乎图的度为全偶还是只有两个奇数作为头尾
我们只需要开一个足够大的二维数组用来储存两个点之间是否连通,然后记录每个点的度,若为奇数则从他开始,
这个题有一点奇怪,,如果我找到第一个奇数点然后break输出倒序就是wa,然后问了紫妈,紫妈说他也wa这里
然后没有break找到结束点往前推,最后顺序输出,就A了,ummm有点迷

AC代码

#include <bits/stdc++.h>
using namespace std;
int mp[30][30]; 
int sum[30];
int idx = 0;
int n,m;
int path[1005];
void dfs(int start){
    for(int i = 1;i <= n;i++){
        if(mp[start][i]){
            mp[start][i] = 0;
            mp[i][start] = 0;
            dfs(i);
        }
    }
    path[idx++] = start;
}
int main(){
    cin >> n >> m;
    for(int i = 1;i <= m;i++){
        int x,y; cin >> x >> y;
        mp[x][y] = mp[y][x] = 1;
        sum[x]++; sum[y]++;
    }
    int from = 1;
    for(int i = 1;i <= n;i++){
        if(sum[i] % 2){
            from = i;
//          break;
        }
    }
    dfs(from);
    for(int i = 0;i < idx - 1;i++) cout << path[i] << " ";
    cout << path[idx-1] << endl;
    return 0;
}

B - 珍珠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轻,所以我们可以移走这两颗珍珠。
    写一个程序统计出共有多少颗珍珠肯定不会是中间重量。

Input

    输入文件第一行包含两个用空格隔开的整数N和M,其中1<=N<=99,且N为奇数,M表示对珍珠进行的比较次数,接下来的M行每行包含两个用空格隔开的整数x和y,表示珍珠x比珍珠y重。

Output

    输出文件仅一行包含一个整数,表示不可能是中间重量的珍珠的总数。

Sample Input

5 4
2 1
4 3
5 1
4 2

Sample Output

2

理解

这一题我起先理解出现了问题,,想的太简单了,总觉得跟建树差不多,如果没有父节点或者没有子节点就是不具备中间变量
然后紫妈说这是floyd闭包传递,,假设珍珠a>b ,b>c 那么a>c;需要一个三循环来解决这个传递问题
然后专门去搜了一下闭包传递三循环的顺序。需要注意的是三层循环的顺序,在我下面的代码中,一定不能颠倒,一定要以i为桥梁连接k和j。
然后进行两次判断,如果比它大的数量超过了(n+1)/2或者比它小的数量(这个题中提到了,划重点),那么这个珍珠就没有中间重量,重点!!!要加等号(我就卡在等号卡了一年?)

AC代码

#include <bits/stdc++.h>
using namespace std;
int mp[105][105];
int main(){
    int n,m; cin >> n >> m;
    memset(mp,0,sizeof(mp));
    for(int i = 1;i <= m;i++){
        int x,y; cin >> x >> y;
        mp[x][y] = 1;
    }
    for(int i = 1;i <= n;i++){
        for(int j = 1;j <= n;j++){
            for(int k = 1;k <= n;k++){
                if(mp[j][i] && mp[i][k]) mp[j][k] = 1;
            }
        }
    }
    int sum;
    int cnt = 0;
    for(int i = 1;i <= n;i++){
        sum = 0;
        for(int j = 1;j <= n;j++){
            if(mp[i][j]) sum++;
        }
        if(sum >= (n+1)/2) cnt++;
        sum = 0;
        for(int j = 1;j <= n;j++){
            if(mp[j][i]) sum++;
        }
        if(sum >= (n+1)/2) cnt++;
    }
    cout << cnt << endl;
    return 0;
}

C - 铲雪车snow

题目描述

    随着白天越来越短夜晚越来越长,我们不得不考虑铲雪问题了。整个城市所有的道路都是双车道,因为城市预算的削减,整个城市只有1辆铲雪车。铲雪车只能把它开过的地方(车道)的雪铲干净,无论哪儿有雪,铲雪车都得从停放的地方出发,游历整个城市的街道。现在的问题是:最少要花多少时间去铲掉所有道路上的雪呢?

Input

    输入数据的第1行表示铲雪车的停放坐标(x,y),x,y为整数,单位为米。下面最多有100行,每行给出了一条街道的起点坐标和终点坐标,所有街道都是笔直的,且都是双向一个车道。铲雪车可以在任意交叉口、或任何街道的末尾任意转向,包括转U型弯。铲雪车铲雪时前进速度为20 km/h,不铲雪时前进速度为50 km/h。
    保证:铲雪车从起点一定可以到达任何街道。

Output

    铲掉所有街道上的雪并且返回出发点的最短时间,精确到分种。

Sample Input

1
0 0
0 0 10000 10000
5000 -10000 5000 10000
5000 10000 10000 10000

Sample Output

3:55

理解

水题!!!纯正水题,,花里胡哨讲了一堆,仔细一想,诶不对啊这不是就是一个欧拉回路吗???而且题目也没说铲雪的地方连续不连续的问题。
然后惹某人就一莽,这不就是去一遍再回一遍(因为题目说街道是双车道)这不就结了吗?为啥还有不铲雪的速度?嘎?
哦对了还要注意一下四舍五入所以+0.5(起先卡在四舍五入失败一次,还被紫妈骂菜,央央央)

AC代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
    ll x,y; cin >> x >> y;
    ll sx,sy,ex,ey;
    double cnt = 0;
    while(~scanf("%lld %lld %lld %lld",&sx,&sy,&ex,&ey)){
        cnt += sqrt((ex-sx)*(ex-sx)+(ey-sy)*(ey-sy));
    }
//  cout << cnt << endl;
    ll h = (ll)(cnt / 10000);
    ll f = (ll)(((cnt / 10000) - h) *60 + 0.5);
    printf("%lld:%02lld\n",h,f);
    return 0;
}

D - 骑马修栅栏

题目描述

    农民John每年有很多栅栏要修理。他总是骑着马穿过每一个栅栏并修复它破损的地方。
    John是一个与其他农民一样懒的人。他讨厌骑马,因此从来不两次经过一个一个栅栏。你必须编一个程序,读入栅栏网络的描述,并计算出一条修栅栏的路径,使每个栅栏都恰好被经过一次。John能从任何一个顶点(即两个栅栏的交点)开始骑马,在任意一个顶点结束。
    每一个栅栏连接两个顶点,顶点用1到500标号(虽然有的农场并没有500个顶点)。一个顶点上可连接任意多(>=1)个栅栏。所有栅栏都是连通的(也就是你可以从任意一个栅栏到达另外的所有栅栏)。
    你的程序必须输出骑马的路径(用路上依次经过的顶点号码表示)。我们如果把输出的路径看成是一个500进制的数,那么当存在多组解的情况下,输出500进制表示法中最小的一个 (也就是输出第一个数较小的,如果还有多组解,输出第二个数较小的,等等)。 输入数据保证至少有一个解。

Input

第1行: 一个整数F(1 <= F <= 1024),表示栅栏的数目
第2到F+1行: 每行两个整数i, j(1 <= i,j <= 500)表示这条栅栏连接i与j号顶点

Output

输出应当有F+1行,每行一个整数,依次表示路径经过的顶点号。注意数据可能有多组解,但是只有上面题目要求的那一组解是认为正确的。

Sample Input

9
1 2
2 3
3 4
4 2
4 5
2 5
5 6
5 7
4 6

Sample Output

1
2
3
4
2
5
4
6
5
7

理解

跟A题挺类似的吧,,就是要求了最小字典序而且两个点间不止一个栅栏(诶这题找到第一个点break就对了,不明所以哭唧唧)
然后本来也是存的数组,结果wa了,后来问了某紫姓大佬,,啊别的大佬都是存栈,然后改了栈,,居然就A了
至于统计点的个数,本来还傻傻的存了个set然后算size(),结果吧。。只要看哪个数最大就好了,,毕竟是从1开始连续的嘛

AC代码

#include <bits/stdc++.h>
using namespace std;
int mp[2000][2000]; 
int sum[5000];
stack <int> s;
int maxn = 0;
int m;
void dfs(int start){
    for(int i = 1;i <= maxn;i++){
        if(mp[start][i]){
            mp[start][i]--;
            mp[i][start]--;
            dfs(i);
        }
    }
    s.push(start);
}
int main(){
    cin >> m;
    for(int i = 1;i <= m;i++){
        int x,y; cin >> x >> y;
        mp[x][y]++,mp[y][x]++;
        sum[x]++; sum[y]++;
        maxn = max(maxn,max(x,y));
    }
    int from = 1;
    for(int i = 1;i <= maxn;i++){
        if(sum[i] % 2){
            from = i;
            break;
        }
    }
    dfs(from);
    while(!s.empty()){
        cout << s.top() << endl;
        s.pop();
    }
    return 0;
}

(二)洛谷

A - P1464 Function

题目描述

对于一个递归函数w(a,b,c)w(a,b,c)
如果a \le 0a≤0 or b \le 0b≤0 or c \le 0c≤0就返回值11.
如果a>20a>20 or b>20b>20 or c>20c>20就返回w(20,20,20)w(20,20,20)
如果a<ba<b并且b<cb<c 就返回w(a,b,c-1)+w(a,b-1,c-1)-w(a,b-1,c)w(a,b,c−1)+w(a,b−1,c−1)−w(a,b−1,c)
其它的情况就返回w(a-1,b,c)+w(a-1,b-1,c)+w(a-1,b,c-1)-w(a-1,b-1,c-1)w(a−1,b,c)+w(a−1,b−1,c)+w(a−1,b,c−1)−w(a−1,b−1,c−1)
这是个简单的递归函数,但实现起来可能会有些问题。当a,b,ca,b,c均为15时,调用的次数将非常的多。你要想个办法才行.
/* absi2011 : 比如 w(30,-1,0)w(30,−1,0)既满足条件1又满足条件2
这种时候我们就按最上面的条件来算
所以答案为1
*/

Input

会有若干行。
并以-1,-1,-1−1,−1,−1结束。
保证输入的数在[-9223372036854775808,9223372036854775807][−9223372036854775808,9223372036854775807]之间,并且是整数。

Output

输出若干行,每一行格式:注意空格。
w(a, b, c) = ans

Sample Input

1 1 1
2 2 2
-1 -1 -1

Sample Output

w(1, 1, 1) = 2
w(2, 2, 2) = 4

理解

这道题考的是递归+记忆搜索,,我以为新手村的boss也不过如此,于是直接递归然后华丽丽的全t了
然后看了一眼标签,,,啊记忆化搜索。。果然是我惹莽夫头太铁了。。于是改了策略,水完了。ummm,啊别忘了long long

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll bg[25][25][25];
ll w(ll a,ll b,ll c){
	if(a <= 0 || b <= 0 || c <= 0) return 1;
	else if(bg[a][b][c]) return bg[a][b][c];
	else if(a > 20 || b > 20 || c > 20) bg[a][b][c] = w(20,20,20);
	else if(a < b && b < c) bg[a][b][c] = w(a,b,c-1)+w(a,b-1,c-1)-w(a,b-1,c);
	else bg[a][b][c] =  w(a-1,b,c)+w(a-1,b-1,c)+w(a-1,b,c-1)-w(a-1,b-1,c-1);
	return bg[a][b][c];
}

int main(){
//	freopen("test.out","w",stdout);
	ll a,b,c;
	while(cin >> a >> b >> c){
		if(a == -1 && b == -1 && c == -1) break;
		memset(bg,0,sizeof(bg));
		printf("w(%lld, %lld, %lld) = ",a,b,c);
		if(a > 20) a = 21;
		if(b > 20) b = 21;
		if(c > 20) c = 21;
		printf("%lld\n",w(a,b,c));
	}
	return 0;
}

B - P2089 烤鸡

题目描述

    猪猪hanke得到了一只鸡
猪猪Hanke特别喜欢吃烤鸡(本是同畜牲,相煎何太急!)Hanke吃鸡很特别,为什么特别呢?因为他有10种配料(芥末、孜然等),每种配料可以放1—3克,任意烤鸡的美味程度为所有配料质量之和
现在,Hanke想要知道,如果给你一个美味程度,请输出这10种配料的所有搭配方案

Input

一行,n<=5000

Output

第一行,方案总数
第二行至结束,10个数,表示每种配料所放的质量
按字典序排列。
如果没有符合要求的方法,就只要在第一行输出一个“0”

Sample Input

11

Sample Output

10
1 1 1 1 1 1 1 1 1 2
1 1 1 1 1 1 1 1 2 1
1 1 1 1 1 1 1 2 1 1
1 1 1 1 1 1 2 1 1 1
1 1 1 1 1 2 1 1 1 1
1 1 1 1 2 1 1 1 1 1
1 1 1 2 1 1 1 1 1 1
1 1 2 1 1 1 1 1 1 1
1 2 1 1 1 1 1 1 1 1
2 1 1 1 1 1 1 1 1 1

理解

啊蠢暴力过的题目(9重循环真滴爽)起先方案数和矩阵顺序反了wa了一次,记录这个水题的理由是为了证明暴力法天下第一

AC代码

#include <bits/stdc++.h>
using namespace std;
int main(){
	int n; scanf("%d",&n);
//	if(n > 30 || n < 10){
//		printf("0\n");
//		return 0;
//	}
	int cnt = 0;
	for(int i = 1;i <= 3;i++)
		for(int j = 1;j <= 3;j++)
			for(int k = 1;k <= 3;k++)
				for(int l = 1;l <= 3;l++)
					for(int x = 1;x <= 3;x++)
						for(int y = 1;y <= 3;y++)
							for(int z = 1;z <= 3;z++)
								for(int q = 1;q <= 3;q++)
									for(int w = 1;w <= 3;w++){
										int r = n-i-j-k-l-x-y-z-q-w;
										if(r >= 1 && r <= 3)
											cnt++;
									}
	cout << cnt << endl;
	for(int i = 1;i <= 3;i++){
		for(int j = 1;j <= 3;j++){
			for(int k = 1;k <= 3;k++){
				for(int l = 1;l <= 3;l++){
					for(int x = 1;x <= 3;x++){
						for(int y = 1;y <= 3;y++){
							for(int z = 1;z <= 3;z++){
								for(int q = 1;q <= 3;q++){
									for(int w = 1;w <= 3;w++){
										int r = n-i-j-k-l-x-y-z-q-w;
										if(r >= 1 && r <= 3){
											printf("%d %d %d %d %d %d %d %d %d %d\n",i,j,k,l,x,y,z,q,w,r);
										}
									}
								}
							}
						}
					}
				}
			}
		}
	}
	return 0;
}

大佬de代码(详见洛谷题解,作者: woshiren)

#include<iostream>
#include<cstdio>
using namespace std;
int n,ans1,ans2[10001][11],sum,a[11];
void trys(int t,int m)//t代表当前的尝试的调料。m代表当前美味程度
{
    if (t>10) 
    {
        if (m==n) //如果美味程度与猪猪的要求相等 
        {
            ans1++;//统计方案总数 
            for (int i=1;i<=10;i++)
            ans2[ans1][i]=a[i];//存入答案的数组 
        }
        return ;
    }
    for (int i=1;i<=3;i++)
    {
        if (m+i>n) break;//如果超过了要求,那么后面的就可以直接忽略 
        a[t]=i;//储存答案 
        trys(t+1,m+i);//查看下一种调料 
        a[t]=0;//状态恢复 
    }
}
int main()
{
    cin>>n;
    trys(1,0);//从第一种调料开始尝试,美味程度为0 
    cout<<ans1<<endl;
    for (int i=1;i<=ans1;i++)
    {
        for (int j=1;j<=10;j++)
            cout<<ans2[i][j]<<" ";
        cout<<endl;
    }//输出结果 
    return 0;
} 

(三)我的感想(海星)

关于这周的日常练习

也有刷洛谷的水题,原本说过年前刷完新手村和普及组?但是好像11月前可以安排掉?
然后图论这块刚开始学,,自己有时候还是会卡,在大佬的帮助下A了四题

关于开始划水的课堂

暂且不论pta上的水题(虽然编程题坑到我)复杂度我也不保证肯定算对
但是这个思考题,比如什么是数据结构,我和紫妈划水了一整个王阳明的选修课讨论了这个问题。
从普通的数组存放到链表的存储再到自定义的struct来说得出了一个结构体是为了解决某些问题所创造的数据集合体的不知道是对是错的自我理解,上课划划水写自己的题的同时也能在偶尔抬头间听到一些新东西。

关于计蒜客

早知道就不去监考了。。遗憾没写几题,还被巡考抓了(现在居然不让提交也找不到题目了,哭辽)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值