第六次练习赛解题报告及标程
来源:互联网 发布:数据结构常用算法 编辑:程序博客网 时间:2024/06/14 21:58
这次练习赛的出题其实非常有针对性,只可惜根据我的调查,体会到这一点的童鞋并不多,好伤心……
A. Who Are My Friends?(Ⅰ)
B. Who Are My Friends?(Ⅱ)
两道上机原题,见上机题解。
C. Who Are My Friends?(Ⅲ)
我们注意到这道题与前两题的唯一区别是,查询过程是在线的,也就是说,边修改边查询。也正因为这个原因,这道题不能再用图论的方法dfs。而并查集的操作上则与之前没有什么不同。
#include<cstdio>using namespace std;const int MAXN=100005;int u[MAXN];void init(){ for(int i=0; i<MAXN; ++i) u[i]=i;}int find(int x){ if(u[x]!=x) u[x]=find(u[x]); return u[x];}void merge(int x,int y){ u[find(x)]=find(y);}bool query(int x,int y){ return find(x)==find(y);}int main(){ int n,k,a,b; while(~scanf("%d",&n)) { init(); while(n--) { scanf("%d%d%d",&k,&a,&b); switch(k) { case 1: merge(a,b); break; case 2: puts(query(a,b)?"Great!":"Pity..."); } } }}
#include<cstdio>using namespace std;const int MAXN=100005;int u[MAXN],r[MAXN];void init(){ for(int i=0; i<MAXN; ++i) { u[i]=i; r[i]=0; }}int find(int x){ if(u[x]!=x) return find(u[x]); return u[x];}void merge(int x,int y){ x=find(x); y=find(y); if(r[x]>r[y]) u[y]=x; else { u[x]=y; if(r[x]==r[y]) ++r[y]; }}bool query(int x,int y){ return find(x)==find(y);}int main(){ int n,k,a,b; while(~scanf("%d",&n)) { init(); while(n--) { scanf("%d%d%d",&k,&a,&b); switch(k) { case 1: merge(a,b); break; case 2: puts(query(a,b)?"Great!":"Pity..."); } } }}
D. 图的DFS遍历
这道题有点无聊,可以说是为了卡内存而卡内存。除了动态管理内存之外,还可以用vector来模拟链表。剩下的就是链表中每个点对应的边应该排好序,然后dfs即可。我直接使用了优先队列和set两种结构来代替vector,用来维护有序性。
#include<cstdio>#include<cstring>#include<vector>#include<queue>using namespace std;const int MAXN=1005;priority_queue<int,vector<int>,greater<int> > g[MAXN];bool vis[MAXN];void dfs(int u){ vis[u]=true; printf("%d ",u); while(!g[u].empty()) { int v=g[u].top(); g[u].pop(); if(!vis[v]) dfs(v); }}int main(){ int n,k,m,u,v; scanf("%d",&n); while(n--) { scanf("%d%d",&k,&m); while(m--) { scanf("%d%d",&u,&v); g[u].push(v); g[v].push(u); } memset(vis,false,sizeof(vis)); for(int i=0; i<k; ++i) if(!vis[i]) dfs(i); putchar('\n'); }}
#include<cstdio>#include<cstring>#include<set>using namespace std;const int MAXN=1005;set<int> g[MAXN];bool vis[MAXN];void dfs(int u){ vis[u]=true; printf("%d ",u); for(set<int>::iterator v=g[u].begin(); v!=g[u].end(); ++v) if(!vis[*v]) dfs(*v);}int main(){ int n,k,m,u,v; scanf("%d",&n); while(n--) { scanf("%d%d",&k,&m); while(m--) { scanf("%d%d",&u,&v); g[u].insert(v); g[v].insert(u); } memset(vis,false,sizeof(vis)); for(int i=0; i<k; ++i) if(!vis[i]) dfs(i); putchar('\n'); for(int i=0; i<k; ++i) g[i].clear(); }}
E. 蒹葭苍苍
单点到单点的最短路,bfs即可。因为有多次询问,写floyd也是一个办法,只是因为询问次数比较少,相比前者会慢一些。我闲得蛋疼写了三份代码。
#include<cstdio>#include<cstring>#include<queue>using namespace std;const int MAXN=305;bool g[MAXN][MAXN],vis[MAXN];int n;int bfs(int a,int b){ queue<pair<int,int> > q; vis[a]=true; q.push(make_pair(a,0)); while(!q.empty()) { pair<int,int> u=q.front(); q.pop(); for(int v=1; v<=n; ++v) if(g[u.first][v]) { if(v==b) return u.second; if(!vis[v]) { vis[v]=true; q.push(make_pair(v,u.second+1)); } } } return -1;}int main(){ int m,k,u,v; while(~scanf("%d%d%d",&n,&m,&k)) { memset(g,false,sizeof(g)); while(m--) { scanf("%d%d",&u,&v); g[u][v]=g[v][u]=true; } while(k--) { scanf("%d%d",&u,&v); memset(vis,false,sizeof(vis)); printf("%d\n",bfs(u,v)); } }}
#include<cstdio>#include<cstring>#include<queue>using namespace std;const int MAXN=305;const int MAXM=20005;struct graph{ int head[MAXN]; int to[MAXM]; int next[MAXM]; int tot; void init() { tot=0; memset(head,0xff,sizeof(head)); } void add(int x,int y) { to[tot]=y; next[tot]=head[x]; head[x]=tot++; }} g;bool vis[MAXN];int bfs(int a,int b){ queue<pair<int,int> > q; vis[a]=true; q.push(make_pair(a,0)); while(!q.empty()) { pair<int,int> u=q.front(); q.pop(); for(int i=g.head[u.first]; ~i; i=g.next[i]) { int v=g.to[i]; if(v==b) return u.second; if(!vis[v]) { vis[v]=true; q.push(make_pair(v,u.second+1)); } } } return -1;}int main(){ int n,m,k,u,v; while(~scanf("%d%d%d",&n,&m,&k)) { g.init(); while(m--) { scanf("%d%d",&u,&v); g.add(u,v); g.add(v,u); } while(k--) { scanf("%d%d",&u,&v); memset(vis,false,sizeof(vis)); printf("%d\n",bfs(u,v)); } }}
#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int MAXN=305;const int INF=0x3f3f3f3f;int g[MAXN][MAXN],n,m,k,u,v;void floyd(){ for(int k=1; k<=n; ++k) for(int i=1; i<=n; ++i) for(int j=1; j<=n; ++j) g[i][j]=min(g[i][j],g[i][k]+g[k][j]);}int main(){ while(~scanf("%d%d%d",&n,&m,&k)) { memset(g,0x3f,sizeof(g)); while(m--) { scanf("%d%d",&u,&v); g[u][v]=g[v][u]=1; } floyd(); while(k--) { scanf("%d%d",&u,&v); printf("%d\n",g[u][v]==INF?-1:g[u][v]-1); } }}
F. 图的环路(Ⅰ)
图的判环是一个很基础的问题,有很多种方式。对于无向图,我这里给出两种方法。
一种是遍历,在遍历过程中如果遇到已经访问过的节点则意味着有环。至于使用dfs还是bfs并不重要。
#include<cstdio>#include<cstring>using namespace std;const int MAXN=10005;const int MAXM=200005;struct graph{ int head[MAXN]; int to[MAXM]; int next[MAXM]; int tot; void init() { tot=0; memset(head,0xff,sizeof(head)); } void add(int x,int y) { to[tot]=y; next[tot]=head[x]; head[x]=tot++; }} g;bool vis[MAXN];bool dfs(int u,int c){ vis[c]=true; bool flag=false; for(int i=g.head[c]; !flag&&~i; i=g.next[i]) { int v=g.to[i]; if(v!=u) { if(vis[v]) return true; flag=dfs(c,v); } } return flag;}int main(){ int n,m,u,v; while(~scanf("%d%d",&n,&m)) { g.init(); while(m--) { scanf("%d%d",&u,&v); g.add(u,v); g.add(v,u); } memset(vis,false,sizeof(vis)); bool flag=false; for(int i=1; !flag&&i<=n; ++i) if(!vis[i]) flag=dfs(0,i); puts(flag?"Yes":"No"); }}
#include<cstdio>#include<cstring>#include<vector>using namespace std;const int MAXN=10005;vector<int> g[MAXN];bool vis[MAXN];bool dfs(int u,int c){ vis[c]=true; bool flag=false; for(int i=0; !flag&&i<g[c].size(); ++i) { int v=g[c][i]; if(v!=u) { if(vis[v]) return true; flag=dfs(c,v); } } return flag;}int main(){ int n,m,u,v; while(~scanf("%d%d",&n,&m)) { while(m--) { scanf("%d%d",&u,&v); g[u].push_back(v); g[v].push_back(u); } memset(vis,false,sizeof(vis)); bool flag=false; for(int i=1; !flag&&i<=n; ++i) if(!vis[i]) flag=dfs(0,i); puts(flag?"Yes":"No"); for(int i=1; i<=n; ++i) g[i].clear(); }}
一种是并查集,不断合并有边相连的两个点,如果合并前两个点已经在一个集合内,则意味着他们在一个环里。
#include<cstdio>#include<cstring>using namespace std;const int MAXN=10005;int f[MAXN];void init(){ for(int i=0; i<MAXN; ++i) f[i]=i;}int find(int x){ if(f[x]!=x) f[x]=find(f[x]); return f[x];}void merge(int x,int y){ f[find(x)]=find(y);}bool query(int x,int y){ return find(x)==find(y);}int main(){ int n,m,u,v; while(~scanf("%d%d",&n,&m)) { bool flag=false; init(); while(m--) { scanf("%d%d",&u,&v); if(query(u,v)) flag=true; merge(u,v); } puts(flag?"Yes":"No"); }}
G. 图的环路(Ⅱ)
对于有向图,这里也给出两种做法。一种仍然是遍历,由于遍历到的已访问过的点不一定意味着成环,只有当这个点是这次遍历的祖先节点时才成环,所以记录状态时要多一个状态:0表示尚未访问,-1表示正在访问子节点,1表示已访问过所有子节点。
#include<cstdio>#include<cstring>using namespace std;const int MAXN=10005;const int MAXM=200005;struct graph{ int head[MAXN]; int to[MAXM]; int next[MAXM]; int tot; void init() { tot=0; memset(head,0xff,sizeof(head)); } void add(int x,int y) { to[tot]=y; next[tot]=head[x]; head[x]=tot++; }} g;int vis[MAXN];bool dfs(int u){ vis[u]=-1; bool flag=false; for(int i=g.head[u]; !flag&&~i; i=g.next[i]) { int v=g.to[i]; if(!~vis[v]) return true; if(!vis[v]) flag=dfs(v); } vis[u]=1; return flag;}int main(){ int n,m,u,v; while(~scanf("%d%d",&n,&m)) { g.init(); while(m--) { scanf("%d%d",&u,&v); g.add(u,v); } memset(vis,0,sizeof(vis)); bool flag=false; for(int i=1; !flag&&i<=n; ++i) if(!vis[i]) flag=dfs(i); puts(flag?"Yes":"No"); }}
#include<cstdio>#include<cstring>#include<vector>using namespace std;const int MAXN=10005;vector<int> g[MAXN];int vis[MAXN];bool dfs(int u){ vis[u]=-1; bool flag=false; for(int i=0; !flag&&i<g[u].size(); ++i) { int v=g[u][i]; if(!~vis[v]) return true; if(!vis[v]) flag=dfs(v); } vis[u]=1; return flag;}int main(){ int n,m,u,v; while(~scanf("%d%d",&n,&m)) { while(m--) { scanf("%d%d",&u,&v); g[u].push_back(v); } memset(vis,0,sizeof(vis)); bool flag=false; for(int i=1; !flag&&i<=n; ++i) if(!vis[i]) flag=dfs(i); puts(flag?"Yes":"No"); for(int i=1; i<=n; ++i) g[i].clear(); }}
另外拓扑排序可以判环。
#include<cstdio>#include<cstring>#include<queue>using namespace std;const int MAXN=10005;const int MAXM=200005;struct graph{ int head[MAXN]; int to[MAXM]; int next[MAXM]; int tot; void init() { tot=0; memset(head,0xff,sizeof(head)); } void add(int x,int y) { to[tot]=y; next[tot]=head[x]; head[x]=tot++; }} g;int du[MAXN],n;bool toposort(){ memset(du,0,sizeof(du)); for(int i=1; i<=n; ++i) for(int j=g.head[i]; ~j; j=g.next[j]) ++du[g.to[j]]; int tot=0; queue<int> q; for(int i=1; i<=n; ++i) if(!du[i]) q.push(i); while(!q.empty()) { int u=q.front(); q.pop(); ++tot; for(int i=g.head[u]; ~i; i=g.next[i]) { int v=g.to[i]; if(!(--du[v])) q.push(v); } } return tot==n;}int main(){ int m,u,v; while(~scanf("%d%d",&n,&m)) { g.init(); while(m--) { scanf("%d%d",&u,&v); g.add(u,v); } puts(!toposort()?"Yes":"No"); }}
#include<cstdio>#include<cstring>#include<vector>#include<queue>using namespace std;const int MAXN=10005;vector<int> g[MAXN];int du[MAXN],n;bool toposort(){ memset(du,0,sizeof(du)); for(int i=1; i<=n; ++i) for(int j=0; j<g[i].size(); ++j) ++du[g[i][j]]; int tot=0; queue<int> q; for(int i=1; i<=n; ++i) if(!du[i]) q.push(i); while(!q.empty()) { int u=q.front(); q.pop(); ++tot; for(int i=0; i<g[u].size(); ++i) { int v=g[u][i]; if(!(--du[v])) q.push(v); } } return tot==n;}int main(){ int m,u,v; while(~scanf("%d%d",&n,&m)) { while(m--) { scanf("%d%d",&u,&v); g[u].push_back(v); } puts(!toposort()?"Yes":"No"); for(int i=1; i<=n; ++i) g[i].clear(); }}
H. Draught
有童鞋过掉这道题还是挺开心的……这道题是CodeChef上的一道原题,算是一个比较弱的树形dp,或者一个比较难的遍历题。第一个问很好做,对于每个连通子图记录节点个数即可;第二个问,节点有气流穿过无非是两种情况,祖先至少有一个节点开窗且子树至少有一个节点开窗,或者子树有至少两个节点开窗,其中子树包括这个节点。两个问可以两次遍历分开求,也可以一次遍历一起做,两种写法稍有不同,代码都会放上来。注意在适当的地方用long long。
#include<cstdio>#include<cstring>using namespace std;const int MAXN=50005;const int MAXM=100005;struct graph{ int head[MAXN]; int to[MAXM]; int next[MAXM]; int tot; void init() { tot=0; memset(head,0xff,sizeof(head)); } void add(int x,int y) { to[tot]=y; next[tot]=head[x]; head[x]=tot++; }} g;bool win[MAXN],air[MAXN];int vis[MAXN],wcnt;void dfs1(int u){ vis[u]=1; if(win[u]) ++wcnt; for(int i=g.head[u]; ~i; i=g.next[i]) { int v=g.to[i]; if(vis[v]==0) dfs1(v); }}int dfs2(int u){ int ret=0,cnt=0; vis[u]=2; if(win[u]) ++ret; for(int i=g.head[u]; ~i; i=g.next[i]) { int v=g.to[i]; if(vis[v]==1) { int tmp=dfs2(v); if(tmp>0) ++cnt; ret+=tmp; } } if(cnt>=2||(cnt>=1&&win[u])||(ret>0&&wcnt>ret)) air[u]=true; return ret;}int main(){ int n,m,u,v; while(~scanf("%d%d",&n,&m)) { for(int i=1; i<=n; ++i) scanf("%d",&win[i]); g.init(); while(m--) { scanf("%d%d",&u,&v); g.add(u,v); g.add(v,u); } memset(vis,0,sizeof(vis)); memset(air,false,sizeof(air)); int fans=0,rans=0; for(int i=1; i<=n; ++i) { if(vis[i]==0) { wcnt=0; dfs1(i); fans+=(long long)wcnt*(wcnt-1)/2; dfs2(i); } if(air[i]) ++rans; } printf("%d %d\n",fans,rans); }}
#include<cstdio>#include<cstring>#include<vector>using namespace std;const int MAXN=50005;vector<int> E[MAXN];bool win[MAXN],air[MAXN];int vis[MAXN],wcnt;void dfs1(int u){ vis[u]=1; if(win[u]) ++wcnt; for(int v=0; v<E[u].size(); ++v) if(vis[E[u][v]]==0) dfs1(E[u][v]);}int dfs2(int u){ int ret=0,cnt=0; vis[u]=2; if(win[u]) ++ret; for(int v=0; v<E[u].size(); ++v) if(vis[E[u][v]]==1) { int tmp=dfs2(E[u][v]); if(tmp>0) ++cnt; ret+=tmp; } if(cnt>=2||(cnt>=1&&win[u])||(ret>0&&wcnt>ret)) air[u]=true; return ret;}int main(){ int n,m,u,v; while(~scanf("%d%d",&n,&m)) { for(int i=1; i<=n; ++i) scanf("%d",&win[i]); while(m--) { scanf("%d%d",&u,&v); E[u].push_back(v); E[v].push_back(u); } memset(vis,0,sizeof(vis)); memset(air,false,sizeof(air)); int fans=0,rans=0; for(int i=1; i<=n; ++i) { if(vis[i]==0) { wcnt=0; dfs1(i); fans+=(long long)wcnt*(wcnt-1)/2; dfs2(i); } if(air[i]) ++rans; } printf("%d %d\n",fans,rans); for(int i=1; i<=n; ++i) E[i].clear(); }}
#include<cstdio>#include<cstring>using namespace std;const int MAXN=50005;const int MAXM=100005;struct graph{ int head[MAXN]; int to[MAXM]; int next[MAXM]; int tot; void init() { tot=0; memset(head,0xff,sizeof(head)); } void add(int x,int y) { to[tot]=y; next[tot]=head[x]; head[x]=tot++; }} g;bool win[MAXN],vis[MAXN],air[MAXN];int src;int dfs(int u){ int ret=0; vis[u]=true; for(int i=g.head[u]; ~i; i=g.next[i]) { int v=g.to[i]; if(!vis[v]) ret+=dfs(v); } if(ret>0||(u!=src&&win[u])) air[u]=true; return ret+win[u];}int main(){ int n,m,u,v; while(~scanf("%d%d",&n,&m)) { for(int i=1; i<=n; ++i) scanf("%d",&win[i]); g.init(); while(m--) { scanf("%d%d",&u,&v); g.add(u,v); g.add(v,u); } memset(vis,false,sizeof(vis)); memset(air,false,sizeof(air)); int fans=0,rans=0; for(int i=1; i<=n; ++i) if(!vis[i]&&win[i]) { src=i; long long wcnt=dfs(i); fans+=wcnt*(wcnt-1)/2; } for(int i=1; i<=n; ++i) if(air[i]) ++rans; printf("%d %d\n",fans,rans); }}
总得来说,这次练习赛重点比较了图论和并查集的相似点和不同点,练习了图论的遍历,加深对图论概念的理解(无向图、有向图、环等),也有一定挑战性的题。至于从中能得到多少收获,全看童鞋们自己了。
- 第六次练习赛解题报告及标程
- 第四次练习赛解题报告及标程
- 第五次练习赛解题报告及标程
- 第七次练习赛解题报告及标程
- 第五次上机赛解题报告及标程
- 第一次练习赛解题报告及标程
- 第三次练习赛解题报告及标程
- C2第六次作业解题报告
- 北京航空航天大学2014第六次上机解题报告
- 20111023练习赛解题报告
- 第一次上机赛解题报告及标程
- 第二次上机赛解题报告及标程
- 期末上机赛解题报告及标程
- 2011新生练习赛三解题报告
- 数论基础练习赛-解题报告
- 牛客练习赛8解题报告
- C++第六次作业报告
- C++第六次实验报告
- Android Handler、Message完全解析,带你从源码的角度彻底理解
- Javascript中最常用的55个经典技巧
- 第四章_回文判断
- 为什么会找不到D层文件?
- 倒水问题学习小记 Poj 1606 + 3414 + Hdu 1495 + UVA 10603
- 第六次练习赛解题报告及标程
- 一个好的libpcap例子
- 由localhost:8080直接进入指定项目
- VMWare三种工作模式
- 函式库管理:ldconfig 与 /etc/ld.so.conf 以及 ldd
- android Standard Broadcast Actions 的对比和锁屏前后的测试
- Huffman java 代码实现
- RTP发送音视频流vlc播放
- hadoop-2.4.0 URLCat