非精写版-51nod基础训练(3)

第一部分地址
第二部分地址

无向图最小生成树

Kruskal算法,基于边选择的方法,适合稠密图。模板:

#include<bits/stdc++.h>
using namespace std;
const int maxn=50050;
struct node{
    int fr,to,w;
}e[maxn];
bool cmp(const node &a,const node &b){
    return a.w<b.w;
}
int father[maxn];
int find(int x){
    if(father[x]==x) return x;
    else father[x]=find(father[x]);
    return father[x];
}
void add(int a,int b){
    father[find(a)]=find(b);
}
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&e[i].fr,&e[i].to,&e[i].w);
    }
    sort(e+1,e+1+m,cmp);
    for(int i=1;i<=n;i++){
        father[i]=i;
    }
    int ans=0;
    for(int i=1,j=1;j<n;i++){
        if(find(e[i].fr)==find(e[i].to)) continue;
        else{
            ans+=e[i].w;
            add(e[i].fr,e[i].to);
            j++;
        }
    }
    printf("%d",ans);
	//system("pause");
    return 0;
}

Prim算法,基于点选择的方法,适合稀疏图。模板:
(本题并不太适用,但也可以AC)

#include<bits/stdc++.h>
using namespace std;
const int maxn=1010;
const double eps=1e-6;
const int INF=0x3f3f3f3f;
typedef long long ll;
int g[maxn][maxn];
int mst[maxn][maxn];
int lowcost[maxn];
int n,m;
int prim(int x){
    int sum=0;
    for(int i=1;i<=n;i++){
        lowcost[i]=g[x][i];
    }
    lowcost[x]=-1;
    for(int i=1;i<=n-1;i++){
        int mind=INF,minid=0;
        for(int j=1;j<=n;j++){
            if(lowcost[j]<mind&&lowcost[j]!=-1){
                mind=lowcost[j];
                minid=j;
            }
        }
        sum+=mind;
        lowcost[minid]=-1;
        for(int j=1;j<=n;j++){
            if(lowcost[j]>g[minid][j]){
                lowcost[j]=g[minid][j];
            }
        }
    }
    return sum;
}
int main(){
    scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                g[i][j]=INF;
            }
        }
        int u,v,w;
        for(int i=0;i<m;i++){
            scanf("%d%d%d",&u,&v,&w);
            g[u][v]=g[v][u]=w;
        }
        int sum=prim(1);
        printf("%d\n",sum);
	//system("pause");
    return 0;
}
威佐夫游戏V2

考虑精度问题的威佐夫游戏。
我们在前文中知道:
第 一 个 值 = 差 值 ∗ ( s q r t ( 5 ) + 1 ) / 2 第一个值 = 差值 *(sqrt(5)+1)/2 =(sqrt(5)+1)/2
( s q r t ( 5 ) + 1 ) / 2 (sqrt(5)+1)/2 (sqrt(5)+1)/2在保留精度时保留不到27位小数,那最后算出来的值因为精度原因造成与n不相等,发生错误。
应手动模拟大数乘法,把18位拆成两个9位储存。
模拟两位数乘以三位数的手写运算。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,m;
ll tmp[3]={618033988,749894848,204586834};
ll mod=1000000000;
int main(){
	int t;
	cin>>t;
	while(t--){
		cin>>m>>n;
		if(m<n) swap(n,m);
		ll c=m-n;
		ll ta=c/mod,tb=c%mod;
		ll tp=tb*tmp[2];
		tp=ta*tmp[2]+tb*tmp[1]+tp/mod;
		tp=ta*tmp[1]+tb*tmp[0]+tp/mod;
		tp=c+ta*tmp[0]+tp/mod;
		if(tp==n) cout<<"B"<<endl;
		else cout<<"A"<<endl;
	}
	//system("pause");
	return 0;
}

已经开始不按照顺序写题解了。
既希望快点完成整个零级题目,又怕囫囵吞枣,知识都是一知半解。


乘法逆元

参考文章1
参考文章2
题目要求: k ∗ m % n = 1 k*m\%n=1 km%n=1,求k的最小正值。
根据公式推导 a ∗ x % n = 1 % n , a = m , x = k a*x\%n=1\%n,a=m,x=k ax%n=1%na=m,x=k我们要求的k就是最小正数解x的值。 a ∗ x + b ∗ y = 1 , b = n a*x+b*y=1,b=n ax+by=1b=n意思为在模n相等的情况下,其实就是相差了y个n。
最终根据扩展欧几里得求出x的值,由于我们需要正整数的解,如果是负数还需要再进一步处理: x = ( x % n + n ) % n x=(x\%n+n)\%n x=(x%n+n)%n
证明完毕。
后面还有一些知识点:

  • 求逆元用扩展欧几里得
  • 若模数是质数可以直接用费马小定理,若不是质数用欧拉函数。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int exgcd(int a,int b,int &x,int &y){
	int d=a;
	if(b!=0){
		d=exgcd(b,a%b,y,x);
		y-=(a/b)*x;
	}else{
		x=1,y=0;
	}
	return d;
}
int moi(int a,int m){
	int x,y;
	exgcd(a,m,x,y);
	return (m+x%m)%m;
}
int main(){
	int n,m;
	cin>>n>>m;
	cout<<moi(n,m)<<endl;
	//system("pause");
	return 0;
}
斐波那契数列的第N项(矩阵快速幂)

[ f ( n ) f ( n − 1 ) ] = [ 1 1 1 0 ] [ f ( n − 1 ) f ( n − 2 ) ] \left[ \begin{matrix} f(n) \\ f(n-1) \\ \end{matrix} \right] = \left[ \begin{matrix} 1 & 1 \\ 1 & 0 \\ \end{matrix} \right] \left[ \begin{matrix} f(n-1) \\ f(n-2) \\ \end{matrix} \right] [f(n)f(n1)]=[1110][f(n1)f(n2)] [ f ( n ) f ( n − 1 ) ] = [ 1 1 1 0 ] [ 1 1 1 0 ] [ f ( n − 2 ) f ( n − 3 ) ] \left[ \begin{matrix} f(n) \\ f(n-1) \\ \end{matrix} \right] = \left[ \begin{matrix} 1 & 1 \\ 1 & 0 \\ \end{matrix} \right] \left[ \begin{matrix} 1 & 1 \\ 1 & 0 \\ \end{matrix} \right] \left[ \begin{matrix} f(n-2) \\ f(n-3) \\ \end{matrix} \right] [f(n)f(n1)]=[1110][1110][f(n2)f(n3)] [ f ( n ) f ( n − 1 ) ] = [ 1 1 1 0 ] n − 1 [ f ( 2 ) f ( 1 ) ] \left[ \begin{matrix} f(n) \\ f(n-1) \\ \end{matrix} \right] = \left[ \begin{matrix} 1 & 1 \\ 1 & 0 \\ \end{matrix} \right] ^{n-1} \left[ \begin{matrix} f(2) \\ f(1) \\ \end{matrix} \right] [f(n)f(n1)]=[1110]n1[f(2)f(1)]根据公式我们可以知道,要求斐波那契数列的第n项,就对矩阵做n-1次乘法运算,故使用矩阵快速幂。
啊啊啊,题目给的是1e9+9,一直模的是1e9+7,调了半个小时。之前矩阵乘法的板子,又试了一遍,无问题。

#include<bits/stdc++.h>
using namespace std;
const int maxn=2;
const int mod=1e9+9;
typedef long long ll;
struct Mat{
    ll m[maxn][maxn];
};
ll n,m;
Mat mul(Mat x,Mat y){
    Mat b;
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            b.m[i][j]=0;
        }
    }
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            for(int k=0;k<n;k++){
                b.m[i][j]=(b.m[i][j]%mod+(x.m[i][k]*y.m[k][j])%mod)%mod;
            }
        }
    }
    return b;
}
Mat qpow(Mat a,ll b){
    Mat res=a;
	if(b<0) return res;
    while(b){
        if(b&1) res=mul(res,a);
        a=mul(a,a);
        b>>=1;
    }
    return res;
}
int main(){
    Mat a,c,t;
	t.m[0][0]=1;
	t.m[1][0]=0;
	t.m[0][1]=0;
	t.m[1][1]=1;
	ll idx;
    cin>>idx;
	n=2;m=2;
    a.m[0][0]=1;
	a.m[0][1]=1;
	a.m[1][0]=1;
	a.m[1][1]=0;
    c=qpow(a,idx-2);
	c=mul(c,t);
    cout<<c.m[0][0]<<endl;
    //system("pause");
    return 0;
}
全排列

要先排一下字典序呀。然后用C++自带的全排列函数即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
char s[10];
int main(){
    scanf("%s",s);
	int len=strlen(s);
	sort(s,s+len);
	printf("%s\n",s);
	while(next_permutation(s,s+len)){
		printf("%s\n",s);
	}
    //system("pause");
    return 0;
}
圆与三角形

要求是给你圆的圆心和半径,给你三角形的三个顶点,问是否相交。
首先很容易想到,求三角形顶点和圆的圆心之间的距离,如果小于半径,则说明该顶点在圆内。

  • 如果三个顶点都在圆内,则一定不相交。
  • 如果有的顶点在圆外,有的顶点在圆内,则一定相交。
  • 如果恰好有顶点到圆心之间的距离为半径,则该点在圆上。
  • 如果三个点都在圆外,那就要分情况讨论了,可能相交,也可能不相交。(在判断的时候记得去找线段到圆心的距离,而非直线到圆心的距离,若线段到圆心的距离大于半径,就算延长线的距离到圆心的距离小于半径,那也不属于三角形)
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
double xc,yc,R;
double xa,xb,xt,ya,yb,yt;
double dis(double x,double xx,double y,double yy){
	return sqrt((x-xx)*(x-xx)+(y-yy)*(y-yy));
}
bool Dis(double x1,double y1,double x2,double y2){
    ll a,b,c,dis1,dis2,an1,an2;
	if(x1==x2){
		a=1;b=0;c=-x1;
	}else if(y1==y2){
		a=0;b=1;c=-y1;
	}else{
		a=y1-y2;
		b=x2-x1;
		c=x1*y2-x2*y1;
	}
	dis1=a*xc+b*yc+c;
	dis1*=dis1;
	dis2=(a*a+b*b)*R;
	if(dis1>dis2){
		return 0;
	}
	an1=(xc-x1)*(x2-x1)+(yc-y1)*(y2-y1);
	an2=(xc-x2)*(x1-x2)+(yc-y2)*(y1-y2);
	if(an1>0&&an2>0) return true;
	return false;
}
int main(){
    int t;
	cin>>t;
	while(t--){
		scanf("%lf%lf%lf",&xc,&yc,&R);
		scanf("%lf%lf",&xa,&ya);
		scanf("%lf%lf",&xb,&yb);
		scanf("%lf%lf",&xt,&yt);
		int d1,d2,d3;
		R=R*R;
		d1=(xc-xa)*(xc-xa)+(yc-ya)*(yc-ya);
		d2=(xc-xb)*(xc-xb)+(yc-yb)*(yc-yb);
		d3=(xc-xt)*(xc-xt)+(yc-yt)*(yc-yt);
		if(d1>R&&d2>R&&d3>R){
			int dis1=Dis(xa,ya,xb,yb);
			int dis2=Dis(xa,ya,xt,yt);
			int dis3=Dis(xb,yb,xt,yt);
			if(dis1||dis2||dis3) printf("Yes\n");
			else printf("No\n");
		}
		else if(d1<R&&d2<R&&d3<R) printf("No\n");
		else printf("Yes\n");
	}
    //system("pause");
    return 0;
}
最短路径(dijkstra优先队列优化板子)
#include<bits/stdc++.h>
using namespace std; 
const int maxn=505;
const int INF=0x3f3f3f3f;
int n,m;
int d[maxn];
vector<pair<int,int> >E[maxn];
void init(){
    for(int i=0;i<maxn;i++) d[i]=INF;
    for(int i=0;i<maxn;i++) E[i].clear();
}
int main(){
    while(cin>>n>>m){
        init();
        int a,b,x;
        for(int i=0;i<m;i++){
            cin>>a>>b>>x;
            E[a].push_back(make_pair(b,x));
            E[b].push_back(make_pair(a,x));
        }
        int s,t;
        cin>>s>>t; 
		d[s]=0;
        priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > >Q;
        Q.push(make_pair(d[s],s));            //优先队列自动根据pair中的first从大到小排序(默认), 放入 负距离,则 边短的先弹出
        while(!Q.empty()){
            int now=Q.top().second;            //显然,now 为一个起点
            Q.pop();
            for(int j=0;j<E[now].size();j++){
                int v=E[now][j].first;         //显然,v 为 now 可以到达的一个终点
                if(d[v]>d[now]+E[now][j].second){    //如果 已知的v 到 s 的距离 大于 now这个点到 s 的距离 + now 这个点到 v 的距离, 更新一下, 并放入优先队列
                    d[v]=d[now]+E[now][j].second;
                    Q.push(make_pair(d[v],v));
                }
            }
        }
        if(d[t]==INF) printf("-1\n");
        else printf("%d\n",d[t]);
    }
	//system("pause");
    return 0;
}
生成树

题面意思:有一个无向图,告诉你要选哪些边,通过操作(每次可以让某个边的边权加一)使选择的这些边得到的最小生成树成立。
思路:只可以增加边权的话一定是在生成树的数边上增加边权的(别令造一些边出来)。我们考虑一条非树边,它的端点在生成树中一定是连通的,那么我们考虑这个环,如果这条非树边一定不再生成树里面,那么它在这个环里面的权值一定是最大的,这里环里面的最大的边可以有很多条,我们只需要求出每一条树的路径上的最大权值边即可,考虑lca求解,最后枚举非树边,计算差值加入答案。


叶子节点的路径

对每个节点的子节点,按照节点编号从小到大排序,这样在遍历树的过程中,路径的字典序是从小到大的。 遍历过程中,用一个数组保存保存从根到当前节点路径中的所有节点。遍历到叶子节点时直接输出即可。

定一个动态数组往里存,该点的子节点都有哪些(对子节点排序,从小到大,满足题目中要求的字典序),然后深搜,一条条搜出来,输出结果。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
const int maxn=2021;
vector<int> g[maxn];
int n;
int q[maxn];
void dfs(int u,int dep){
	q[dep]=u;
	if(g[u].size()==0){
		for(int i=1;i<=dep;i++){
			cout<<q[i]<<" ";
		}
		cout<<endl;
		return ;
	}
	for(int i=0;i<g[u].size();i++){
		int v=g[u][i];
		dfs(v,dep+1);
	}
}
int main(){
	cin>>n;
	for(int i=1;i<n;i++){
		int a,b;
		cin>>a>>b;
		g[a].push_back(b);
	}
	for(int i=1;i<=n;i++){
		sort(g[i].begin(),g[i].end());
	}
	dfs(1,1);
	//system("pause");
	return 0;
}
凌波微步

建图共 8个点,Me, Home ,传送门1起点,传送门1终点,传送门2起点,传送门2终点,传送门3起点,传送门3终点。传送门起点与终点的边权为 10,其他点之间的边权为两点间的曼哈顿距离,即 |Xv−Xu|+|Yv−Yu|。 建图后跑最短路即可。

还是看了一眼别人的代码,有细节没想到(应该也可以想到,懒了)
一共四对起点终点,每对里面是一组坐标。进去深搜,从起点开始,当k==2时表示传送门全使了一遍。(0,1,2)思考为什么必须要用传送门,要是传送门的距离不如我走过去的曼哈顿距离,那就不用,不用的代码就是那句很突兀的dfs(k+1,x,y,sum);这样回溯的话表示没有选择传送门,且k增加了。

补充:如果是正常的最短路,是点与点连接,现在是坐标与坐标连接,就没办法写到二维数组中,可以写到四维数组中储存边权,但是坐标点的范围是1e9,过于大,内存一定会爆掉。可以把坐标标号,然后离散化,只记录当前几个标号点间的边权,一共八个点。但是计算曼哈顿路径的时候比较麻烦(也不麻烦)。吐血调试一个小时,代码写在下面了,是因为Floyd写错了,(k,i,j),k一直写在了第三层,应该写在第一层。写在第三层最后状态转移不回最初的起点到终点。

#include<bits/stdc++.h>
using namespace std; 
const int INF=0x3f3f3f3f;
typedef long long ll;
struct node{
    ll x,y;
    ll a,b;
}me,home,Node[4];
ll ans=INF;
int vis[4];
ll dis(ll x,ll y,ll a,ll b){
    return abs(x-a)+abs(y-b);
}
void dfs(int k,ll x,ll y,ll sum){
    if(k==2){
        ans=min(ans,sum+dis(x,y,home.x,home.y));
        return ;
    }
    dfs(k+1,x,y,sum);
    for(int i=0;i<3;i++){
        if(!vis[i]){
            vis[i]=1;
            dfs(k+1,Node[i].x,Node[i].y,sum+dis(x,y,Node[i].a,Node[i].b)+10);
            dfs(k+1,Node[i].a,Node[i].b,sum+dis(x,y,Node[i].x,Node[i].y)+10);
            vis[i]=0;
        }   
    }
}
int main(){
    cin>>me.x>>me.y>>home.x>>home.y;
    for(int i=0;i<3;i++){
        cin>>Node[i].x>>Node[i].y>>Node[i].a>>Node[i].b;
    }
    dfs(0,me.x,me.y,0);
    cout<<ans<<endl;
	//system("pause");
    return 0;
}

AC题解版:

#include<bits/stdc++.h>
using namespace std; 
const int INF=0x3f3f3f3f;
typedef long long ll;
struct node{
    ll x,y;
    ll a,b;
}me,home,Node[4];
ll ans=INF;
int vis[4];
ll dis(ll x,ll y,ll a,ll b){
    return abs(x-a)+abs(y-b);
}
void dfs(int k,ll x,ll y,ll sum){
    if(k==2){
        ans=min(ans,sum+dis(x,y,home.x,home.y));
        return ;
    }
    dfs(k+1,x,y,sum);
    for(int i=0;i<3;i++){
        if(!vis[i]){
            vis[i]=1;
            dfs(k+1,Node[i].x,Node[i].y,sum+dis(x,y,Node[i].a,Node[i].b)+10);
            dfs(k+1,Node[i].a,Node[i].b,sum+dis(x,y,Node[i].x,Node[i].y)+10);
            vis[i]=0;
        }   
    }
}
int main(){
    cin>>me.x>>me.y>>home.x>>home.y;
    for(int i=0;i<3;i++){
        cin>>Node[i].x>>Node[i].y>>Node[i].a>>Node[i].b;
    }
    dfs(0,me.x,me.y,0);
    cout<<ans<<endl;
	system("pause");
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
SQLAlchemy 是一个 SQL 工具包和对象关系映射(ORM)库,用于 Python 编程语言。它提供了一个高级的 SQL 工具和对象关系映射工具,允许开发者以 Python 类和对象的形式操作数据库,而无需编写大量的 SQL 语句。SQLAlchemy 建立在 DBAPI 之上,支持多种数据库后端,如 SQLite, MySQL, PostgreSQL 等。 SQLAlchemy 的核心功能: 对象关系映射(ORM): SQLAlchemy 允许开发者使用 Python 类来表示数据库表,使用类的实例表示表中的行。 开发者可以定义类之间的关系(如一对多、多对多),SQLAlchemy 会自动处理这些关系在数据库中的映射。 通过 ORM,开发者可以像操作 Python 对象一样操作数据库,这大大简化了数据库操作的复杂性。 表达式语言: SQLAlchemy 提供了一个丰富的 SQL 表达式语言,允许开发者以 Python 表达式的方式编写复杂的 SQL 查询。 表达式语言提供了对 SQL 语句的灵活控制,同时保持了代码的可读性和可维护性。 数据库引擎和连接池: SQLAlchemy 支持多种数据库后端,并且为每种后端提供了对应的数据库引擎。 它还提供了连接池管理功能,以优化数据库连接的创建、使用和释放。 会话管理: SQLAlchemy 使用会话(Session)来管理对象的持久化状态。 会话提供了一个工作单元(unit of work)和身份映射(identity map)的概念,使得对象的状态管理和查询更加高效。 事件系统: SQLAlchemy 提供了一个事件系统,允许开发者在 ORM 的各个生命周期阶段插入自定义的钩子函数。 这使得开发者可以在对象加载、修改、删除等操作时执行额外的逻辑。
SQLAlchemy 是一个 SQL 工具包和对象关系映射(ORM)库,用于 Python 编程语言。它提供了一个高级的 SQL 工具和对象关系映射工具,允许开发者以 Python 类和对象的形式操作数据库,而无需编写大量的 SQL 语句。SQLAlchemy 建立在 DBAPI 之上,支持多种数据库后端,如 SQLite, MySQL, PostgreSQL 等。 SQLAlchemy 的核心功能: 对象关系映射(ORM): SQLAlchemy 允许开发者使用 Python 类来表示数据库表,使用类的实例表示表中的行。 开发者可以定义类之间的关系(如一对多、多对多),SQLAlchemy 会自动处理这些关系在数据库中的映射。 通过 ORM,开发者可以像操作 Python 对象一样操作数据库,这大大简化了数据库操作的复杂性。 表达式语言: SQLAlchemy 提供了一个丰富的 SQL 表达式语言,允许开发者以 Python 表达式的方式编写复杂的 SQL 查询。 表达式语言提供了对 SQL 语句的灵活控制,同时保持了代码的可读性和可维护性。 数据库引擎和连接池: SQLAlchemy 支持多种数据库后端,并且为每种后端提供了对应的数据库引擎。 它还提供了连接池管理功能,以优化数据库连接的创建、使用和释放。 会话管理: SQLAlchemy 使用会话(Session)来管理对象的持久化状态。 会话提供了一个工作单元(unit of work)和身份映射(identity map)的概念,使得对象的状态管理和查询更加高效。 事件系统: SQLAlchemy 提供了一个事件系统,允许开发者在 ORM 的各个生命周期阶段插入自定义的钩子函数。 这使得开发者可以在对象加载、修改、删除等操作时执行额外的逻辑。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值