这次考试是一个图论专练,从海亮回来图论已经搞了好几个月了,感觉有些问题的思想还是要好好揣摩一下,理解思路才能知道怎么做;
第一题:
第一遍没看见有向图,建了个图跑了个tarjan,后来发现读错题,我们来看一下谜一样的水数据,这O(N*M)的复杂都能过;
随便写一个dfs吧,当前点遍历到的点的个数+1,如果遍历N个点,那么满足条件,记录下来,没什么可说的了;
然后数据没让我失望,A了;
#include<bits/stdc++.h> using namespace std; #define N 500010 template<typename T>inline void read(T &x) { x=0;T f=1,ch=getchar(); while(!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48); ch=getchar();} x*=f; } int n,m,x,y,tot,cnt,num,root,dfn[N],low[N],ans[N],v[N],lin[N]; struct gg { int y,next; }a[N<<1]; inline void add(int x,int y) { a[++tot].y=y; a[tot].next=lin[x]; lin[x]=tot; } void dfs(int x) { for(int i=lin[x];i;i=a[i].next) if(!v[a[i].y]) ++cnt,v[a[i].y]=1,dfs(a[i].y); } int main() { freopen("flow.in","r",stdin); freopen("flow.out","w",stdout); read(n); read(m); for(int i=1,a,b;i<=m;++i) read(a),read(b),add(a, b); int k=0; for(int i=1;i<=n;++i) { memset(v,0,sizeof(v)); cnt=1,v[i]=1; dfs(i); if(cnt==n) ans[++k]=i; } if(!k) { putchar('0'); return 0; } printf("%d ",k); for(int i=1;i<=k;++i) printf("%d ",ans[i]); return 0; }
第二题:
我们来简化一下题意哈:
每次选择两个点异或起来,再删除一个直至剩一个点,并且保证异或和最大;
刚拿到题:以为是个trie树,正准备写,可是我不会怎么处理每次删除哪一个会更优;
后来想了想,是个最大生成树,然后每两个点之间的权值是异或值,跑一个Prim或者Kruskal,考场上sb的推翻自己以为是基于点,我非得写Prim;
数据还是没让人失望,让我这个sbA了;
那没事,我现在补齐两种做法;
Prim:
#include<bits/stdc++.h> using namespace std; #define N 2500 #define ll long long template<typename T>inline void read(T &x) { x=0;T f=1,ch=getchar(); while(!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48); ch=getchar();} x*=f; } ll ans=0; int n,a[N],Map[N][N],dis[N],vis[N]; inline void Prim_X() { vis[1]=1; for(int i=1;i<=n;i++) dis[i]=Map[1][i]; for(int i=1;i<n;i++) { int y=0; for(int j=1;j<=n;j++) if(!vis[j]&&dis[j]>dis[y]) y=j; ans+=dis[y]; vis[y]=1; for(int j=1;j<=n;j++) { if(!vis[j]) { dis[j]=max(dis[j],Map[y][j]); } } } } int main() { freopen("bull.in","r",stdin); freopen("bull.out","w",stdout); read(n); for(int i=1;i<=n;i++) read(a[i]); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) Map[i][j]=a[i]^a[j]; Prim_X(); cout<<ans<<endl; return 0; }
Kruskal:
#include<bits/stdc++.h> using namespace std; #define N 50010 int n,k,tot,h[N],father[N]; long long ans; template<typename T>inline void read(T &x) { x=0;T f=1,ch=getchar(); while(!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48); ch=getchar();} x*=f; } struct gg { int x,y; long long v; }a[N<<1]; inline int find(int x) { return father[x]==x?x:father[x]=find(father[x]); } bool mycmp(gg x,gg y) { return x.v>y.v; } int main() { read(n); for(int i=1;i<=n;i++) { father[i]=i; read(h[i]); for(int j=1;j<i;j++) { a[++tot].x=i; a[tot].y=j; a[tot].v=h[i]^h[j]; } } sort(a+1,a+tot+1,mycmp); k=0; for(int i=1;i<=tot;i++) { int x=find(a[i].x),y=find(a[i].y); if(x==y) continue; father[y]=x; ans+=a[i].v; k++; if(k==n-1) break; } cout<<ans<<endl; return 0; }
第三题:
re了一个点,? 。跑N边spfa的人都过了,;
我们往常写的都是一对多的spfa,那么今天写一个多对一的spfa,反向建图;
对于每个点我们要求出这个点到点X再回来的最短路,
点X到这个点的最短路只需要跑一遍以点X为源点的最短路就行了,
但是每个点到点X的最短路怎么求呢?把边反向之后再跑一遍以点X为源点的最短路就行了。
代码;考场写的代码太丑了,所以又写了一遍;
#include<bits/stdc++.h> using namespace std; #define inf 0x3f3f3f #define N 50010 template<typename T>inline void read(T &x) { x=0;T f=1,ch=getchar(); while(!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48); ch=getchar();} x*=f; } int n,m,s,tot,cnt,a,b,l,dis1[N],dis2[N],lin1[N],lin2[N],vis[N]; struct gg { int x,y,v,next; }e1[N<<1],e2[N<<1]; inline void add1(int x,int y,int v) { e1[++tot].y=y; e1[tot].next=lin1[x]; e1[tot].v=v; lin1[x]=tot; } inline void add2(int x,int y,int v) { e2[++cnt].y=y; e2[cnt].next=lin2[x]; e2[cnt].v=v; lin2[x]=cnt; } inline void spfa1() { memset(vis,0,sizeof(vis)); queue<int> q; q.push(s); vis[s]=1;dis1[s]=0; while(!q.empty()) { int x=q.front(); q.pop(); vis[x]=0; for(int i=lin1[x];i!=0;i=e1[i].next) { if(dis1[x]+e1[i].v<dis1[e1[i].y]) { dis1[e1[i].y]=dis1[x]+e1[i].v; if(!vis[e1[i].y]) { vis[e1[i].y]=1; q.push(e1[i].y); } } } } } inline void spfa2() { queue<int> q; memset(vis,0,sizeof(vis)); q.push(s); vis[s]=1;dis2[s]=0; while(!q.empty()) { int x=q.front(); q.pop(); vis[x]=0; for(int i=lin2[x];i!=0;i=e2[i].next) { if(dis2[x]+e2[i].v<dis2[e2[i].y]) { dis2[e2[i].y]=dis2[x]+e2[i].v; if(!vis[e2[i].y]) { vis[e2[i].y]=1; q.push(e2[i].y); } } } } } int main() { read(n); read(m); read(s); memset(dis1,127,sizeof(dis1)); memset(dis2,127,sizeof(dis2)); for(int i=1;i<=m;i++) { read(a);read(b);read(l); e1[i].y=b; e1[i].next=lin1[a]; lin1[a]=i; e1[i].v=l; e2[i].y=a; e2[i].next=lin2[b]; e2[i].v=l; lin2[b]=i;//反向建边; } spfa1(); spfa2(); int ans=0; for(int i=1;i<=n;i++) ans=max(ans,dis1[i]+dis2[i]); cout<<ans<<endl; return 0; }
分数:290分,另外10分献给sb的自己;
本来计划最近复习图论,结果直接考了,最近计划复习图论考点,写写以前的经典题目,还要学搜索最近,加油!