比赛地址:点击打开链接
A题(POJ 3984):
解题报告:BFS,貌似丁神的写法更好一些。
#include<iostream> #include<cstdio> #include<queue> using namespace std; struct node { int x,y; int pre; }; queue<node> q; int map[11][11]; node way[111]; int dir[4][2]={{-1,0},{0,1},{1,0},{0,-1}}; bool inside(int x,int y) { return (x>=0&&x<5&&y>=0&&y<5&&map[x][y]==0); } void find_way(int i) { if(i==-1) return; find_way(way[i].pre); printf("(%d, %d)\n",way[i].x,way[i].y); return; } void bfs(int x,int y) { node temp; way[0].x=x,way[0].y=y,way[0].pre=-1; map[0][0]=1; q.push(way[0]); while(!q.empty()) { temp=q.front(); q.pop(); for(int i=0;i<4;i++) { int xx=temp.x+dir[i][0]; int yy=temp.y+dir[i][1]; if(xx==4&&yy==4) { way[xx*5+yy].pre=5*temp.x+temp.y; way[xx*5+yy].x=4,way[xx*5+yy].y=4; find_way(xx*5+yy); } if(inside(xx,yy)) { way[xx*5+yy].pre=5*temp.x+temp.y; way[xx*5+yy].x=xx,way[xx*5+yy].y=yy; map[xx][yy]=1; q.push(way[xx*5+yy]); } } } } int main() { for(int i=0;i<5;i++) for(int j=0;j<5;j++) scanf("%d",&map[i][j]); bfs(0,0); return 0; }
B题(POJ 1149):网络流
解题报告:http://www.cnblogs.com/sleeper-qp/archive/2012/07/23/2605253.html
#include <algorithm> #include <iostream> #include <iomanip> #include <cstring> #include <climits> #include <complex> #include <fstream> #include <cassert> #include <cstdio> #include <bitset> #include <vector> #include <deque> #include <queue> #include <stack> #include <ctime> #include <set> #include <map> #include <cmath> using namespace std; const int INF=1<<30; int cap[105][105],flow[105][105]; int rflow[105],pre[105]; int m,n,x,y,u,v,c,maxflow,st,ed,e; int pign[1005]; int connect[1005]; bool vis[1005]; int main() { scanf("%d %d",&n,&m); memset(cap,0,sizeof(cap)); memset(flow,0,sizeof(flow)); memset(vis,0,sizeof(vis)); memset(connect,0,sizeof(connect)); st = m + 1; ed = m + 2; for(int i = 1;i <= n;++i) scanf("%d",&pign[i]); for(x = 1;x <= m;++x)//构图十分重要 { scanf("%d",&e); while(e--) { scanf("%d",&y); if(!vis[y]) { cap[st][x] += pign[y];//合并流量 vis[y] = 1; connect[y] = x; } else cap[connect[y]][x] = INF; } scanf("%d",&c); cap[x][ed] = c; } for(;;)//最大流模板 { queue<int> q; memset(rflow,0,sizeof(rflow)); memset(pre,0,sizeof(pre)); rflow[st] = INF; q.push(st); while(!q.empty()) { u = q.front(); q.pop(); for(int v = 0;v <= ed;++v) { if(!rflow[v] && cap[u][v] > flow[u][v]) { pre[v] = u; rflow[v] = min(rflow[u],cap[u][v] - flow[u][v]); q.push(v); } } } if(rflow[ed] == 0) break; for(u = ed;u != st;u = pre[u]) { flow[pre[u]][u] += rflow[ed]; flow[u][pre[u]] -= rflow[ed]; } maxflow += rflow[ed]; } printf("%d\n",maxflow); return 0; }
C题(POJ 3249):拓扑排序+DP(详细算法参见算法导论),模板题
解题报告:单源最短路径问题(点权转化为边权),有负权值,有环,用Dijkstra据说会超时。
#include <algorithm> #include <iostream> #include <iomanip> #include <cstring> #include <climits> #include <complex> #include <fstream> #include <cassert> #include <cstdio> #include <bitset> #include <vector> #include <deque> #include <queue> #include <stack> #include <ctime> #include <set> #include <map> #include <cmath> #define clear(a) memset((a),0,sizeof(a)) using namespace std; #define MAXN 100100 #define INF 0x3FFFFFF struct node { int u,v; //int dis; int next; }e[99999999]; int dp[MAXN],c[MAXN]; int head[MAXN],en; bool vis[MAXN]; int n,m; void add(int u,int v) { e[en].u=u; e[en].v=v; e[en].next=head[v]; head[v]=en++; } int DAGShortestPath(int x) { if(vis[x]) return dp[x]; for(int i=head[x];i!=-1;i=e[i].next) dp[x]=max(dp[x],DAGShortestPath(e[i].u)); vis[x]=1; if(head[x]==-1) dp[x]=c[x]; else dp[x]+=c[x]; return dp[x]; } int out[MAXN]; int main() { while(~scanf("%d%d",&n,&m)) { for(int i=1;i<=n;i++) { dp[i]=-INF; scanf("%d",&c[i]); } int x,y; memset(out,0,sizeof(out)); memset(head,-1,sizeof(head)); en=0; for(int i=0;i<m;i++) { scanf("%d%d",&x,&y); add(x,y); out[x]++; } int ans=-INF; memset(vis,0,sizeof(vis)); for(int i=1;i<=n;i++) if(!out[i]) ans=max(ans,DAGShortestPath(i)); printf("%d\n",ans); } return 0; }
D题(POJ 2186):缩环,计算强连通分量(http://www.cnblogs.com/saltless/archive/2010/11/08/1871430.html)用的是tarjan算法,证明见链接
解题报告:遍历新图的各个点,计算其出度,如果只有一个点出度为0,则输出其内部点的个数(缩环后),如果有多个为0,说明没有答案。
#include <algorithm> #include <iostream> #include <iomanip> #include <cstring> #include <climits> #include <complex> #include <fstream> #include <cassert> #include <cstdio> #include <bitset> #include <vector> #include <deque> #include <queue> #include <stack> #include <ctime> #include <set> #include <map> #include <cmath> using namespace std; const int N=10002; const int M=50002; typedef struct node { int v; int next; }; node edge[M]; int head[N]; int n,m; int scc;//强连通分量 int index;//每个节点的访问次序编号 时间戳 int cnt;//边的数量 int vis[N];//标记结点i的时间戳 int low[N];//记录节点u或u的子树中的所有节点的最小标号 int belong[N];//属于哪个分支 bool instack[N];//是否在栈中 int num[N];//记录一个强连通分量中结点的个数 int sstack[N]; int top; int out[N];//出度 void addedge(int u,int v) { edge[cnt].v=v; edge[cnt].next=head[u]; head[u]=cnt++; } void tarjan(int u) { vis[u]=low[u]=++index; sstack[++top]=u; instack[u]=true; for (int j=head[u];j!=-1;j=edge[j].next) { int v=edge[j].v; if(vis[v]==0)//未曾访问过 { tarjan(v); low[u]=min(low[u],low[v]); } else if(instack[v]) low[u]=min(low[u],vis[v]); } if(vis[u]==low[u]) { scc++; while(1) { int tmp=sstack[top--]; instack[tmp]=0; belong[tmp]=scc; num[scc]++; if(tmp==u) break; } } } void solve() { scc=top=index=0; int outnum; memset(num,0,sizeof(num)); memset(vis,0,sizeof(vis)); for (int i=1;i<=n;i++) if (!vis[i]) tarjan(i); memset(out,0,sizeof(out)); for (int i=1;i<=n;i++) for(int j=head[i];j!=-1;j=edge[j].next) { int u=belong[i]; int v=belong[edge[j].v]; if(u!=v) out[u]=1; } outnum=0; int aug; for(int i=1;i<=scc;i++) { if(!out[i]) { outnum++; aug=i; } } if(outnum==1) printf("%d\n",num[aug]); else puts("0"); } int main() { while (scanf("%d%d",&n,&m)!=EOF) { cnt=0; memset(head,-1,sizeof(head)); for (int i=1;i<=m;i++) { int a,b; scanf("%d%d",&a,&b); addedge(a,b); } solve(); } }
E题(POJ 1511):spfa
解题报告:有向图,从起点与终点用两次spfa,然后直接计算权值和。有坑long long.....
<pre name="code" class="cpp">#include <algorithm> #include <iostream> #include <iomanip> #include <cstring> #include <climits> #include <complex> #include <fstream> #include <cassert> #include <cstdio> #include <bitset> #include <vector> #include <deque> #include <queue> #include <stack> #include <ctime> #include <set> #include <map> #include <cmath>using namespace std;const long long INF = 1LL<<63-1;const int N=1000010;int n, m, ww;long long d[N];int cnt;struct node{ int to; int next;//每次插边的时候是插在已插的边前面,所以已插的边是新边的next int weight;}e[N];int lastshow[N];bool inqueue[N];queue<int>q;void insert(int a, int b, int w){ e[++cnt].to = b; e[cnt].weight = w; e[cnt].next = lastshow[a]; lastshow[a] = cnt;//lastshow记录一个点上次作为起点的边的序号,所以一条边的next是它的起点的lastshow值}bool spfa(){ q.push(1); while(!q.empty()){ int x = q.front(); q.pop(); inqueue[x] = false; int id = lastshow[x]; while(id != -1){ if(d[x] < INF && d[e[id].to] > e[id].weight + d[x]){ d[e[id].to] = e[id].weight + d[x]; if(!inqueue[e[id].to]){ inqueue[e[id].to] = true; q.push(e[id].to); } } id = e[id].next; } } return false;}void init(){ int i; for(i = 1; i <= n; i ++){ lastshow[i] = -1; inqueue[i] = false; } for(i = 1; i <= n; ++ i) d[i]=INF; d[1]=0; cnt=0; while(!q.empty()){ q.pop(); }}int a[N], b[N], w[N];int main(){ int T; int i; long long sum; cin >> T; while(T -- ){ sum = 0; cnt = 0; scanf("%d %d", &n, &m); init(); for(i = 1; i <= m; i ++){ scanf("%d %d %d", &a[i], &b[i], &w[i]); insert(a[i], b[i], w[i]); } spfa(); for(i = 2; i <= n; i ++) sum += d[i]; init(); for(i = 1; i <= m; i ++) insert(b[i], a[i], w[i]); spfa(); for(i = 2; i <= n; i ++) sum += d[i]; printf("%lld\n",sum); } return 0;}
F题(POJ 2513):Trie树+欧拉环判断
解题报告:模板题,不解释
#include <algorithm> #include <iostream> #include <iomanip> #include <cstring> #include <climits> #include <complex> #include <fstream> #include <cassert> #include <cstdio> #include <bitset> #include <vector> #include <deque> #include <queue> #include <stack> #include <ctime> #include <set> #include <map> #include <cmath> using namespace std; const int MAXN=510005; struct node { int x; node *next[26]; }; int deg[MAXN], father[MAXN], cnt, ant; node *root, memory[MAXN*10]; node *create()//字典树模板 { node *p=&memory[ant++]; int i; for(i=0;i<26;i++) p->next[i] = NULL; return p; }; void insert(char *s, int x) { node *p=root; for(int i=0;s[i];i++) { int k=s[i]-'a'; if(p->next[k] == NULL) p->next[k] = create(); p=p->next[k]; } p->x=x; } int search(char *s) { node *p = root; int i, k; for(int i=0;s[i];i++) { int k=s[i]-'a'; if(p->next[k]==NULL) return 0; p=p->next[k]; } return p->x; } void init() { int i; for(i = 1; i <= MAXN; i++) father[i] = i; memset(deg, 0, sizeof(deg)); cnt = 0; } int findfa(int x)//并查集模板 { if(x!=father[x]) father[x]=findfa(father[x]); return father[x]; } void Merge(int x, int y) { int xx=findfa(x); int yy=findfa(y); if(xx!=yy) father[xx]=yy; } bool judge()//欧拉环判断 { int odd=0; for(int i=1;i<=cnt;i++) if(deg[i]%2==1) odd++; if(odd!=0&&odd!=2) return false; int k=findfa(1); for(int i=2;i<=cnt;i++) if(k!=findfa(i)) return false; return true; } int main() { int x, y, fx, fy; char s1[15], s2[15]; init(); root=create(); while(scanf("%s %s", s1, s2) != EOF) { x=search(s1); y=search(s2); if(x==0) insert(s1, x=++cnt); if(y==0) insert(s2, y=++cnt); deg[x]++; deg[y]++; Merge(x,y);//将所有可以连接在一起的sitck连在一起 } if(judge()) printf("Possible\n"); else printf("Impossible\n"); return 0; }
G题(POJ 2491):STL>map的应用
解题报告:注意map的使用,别超时。
#include <algorithm> #include <iostream> #include <iomanip> #include <cstring> #include <climits> #include <complex> #include <fstream> #include <cassert> #include <cstdio> #include <bitset> #include <vector> #include <deque> #include <queue> #include <ctime> #include <set> #include <map> #include <cmath> using namespace std; int main() { int T,ca=1; int n; string str1,str2; scanf("%d",&T); while(T--) { scanf("%d",&n); map<string,string> s; map<string,int> e; for(int i=0;i<n-1;i++) { cin>>str1>>str2; s[str1]=str2; e[str1]++; e[str2]++; } string head; for(map<string,int>::iterator it=e.begin();it!=e.end();it++) { if(it->second==1&&s[it->first].size()) head=it->first; } printf("Scenario #%d:\n",ca++); while(s[head].size()) { cout<<head<<endl; head=s[head]; } cout<<head<<endl; cout<<endl; } return 0; }
J题(HDU 4081):次小生成树
解题报告:为了使A/B最大,就应该是B越小,故可以先求出n个点的最小生成树。因此,可以枚举每一条边,假设最小生成树的值是B, 而枚举的那条边长度是edge[i][j], 如果这一条边已经是属于最小生成树上的,那么最终式子的值是A/(B-edge[i][j])。如果这一条不属于最小生成树上的, 那么添加上这条边,就会有n条边,那么就会使得有了一个环,为了使得它还是一个生成树,就要删掉环上的一棵树。 为了让生成树尽量少,那么就要删掉除了加入的那条边以外,权值最大的那条路径。 假设删除的那个边的权值是Max[i][j], 那么就是A/(B-Max[i][j]).
这题的关键也在于怎样求出次小生成树;
先用prim求出最小生成树T.在prim的同时,用一个矩阵max[u][v] 记录 在T中连结任意两点u,v的唯一的路中权值最大的那条边的权值.这是很容易做到的,因为prim是每次增加一个结点s, 而设已经标号了的结点集合为W, 则W中所有的结点到s的路中的最大权值的边就是当前加入的这条边.
<pre name="code" class="cpp">#include <algorithm> #include <iostream> #include <iomanip> #include <cstring> #include <climits> #include <complex> #include <fstream> #include <cassert> #include <cstdio> #include <bitset> #include <vector> #include <deque> #include <queue> #include <stack> #include <ctime> #include <set> #include <map> #include <cmath>const int N=1010;const double inf=1e14;using namespace std;struct Point{ int x,y,z;}point[N];int n;double edge[N][N];int nearvex[N];//保存前驱double lowcost[N]; double sum;int used[N][N];int visited[N];double Max[N][N];//用来保存最小生成树中两点之间的权值最大的边void prim(int v0){ sum=0; memset(used,0,sizeof(used)); memset(visited,0,sizeof(visited)); memset(Max,0,sizeof(Max)); for(int i=1;i<=n;i++){ lowcost[i]=edge[v0][i]; nearvex[i]=v0; } visited[v0]=1; for(int i=1;i<n;i++){ double min=inf; int v=-1; for(int j=1;j<=n;j++){ if(!visited[j]&&lowcost[j]<min){ v=j,min=lowcost[j]; } } if(v!=-1){ sum+=lowcost[v]; used[v][nearvex[v]]=used[nearvex[v]][v]=1;//标记这条边已经是最小使用过// visited[v]=1; for(int k=1;k<=n;k++){ if(visited[k]&&k!=v){ //对于那些已经加入最小生成树的边,只要每次更新所有点到新加入的点之间的边权值最大值即可 Max[v][k]=Max[k][v]=(Max[k][nearvex[v]]>lowcost[v]?Max[k][nearvex[v]]:lowcost[v]); } if(!visited[k]&&edge[v][k]<lowcost[k]){ lowcost[k]=edge[v][k]; nearvex[k]=v; } } } }}int main(){ int t; scanf("%d",&t); while(t--){ scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d%d%d",&point[i].x,&point[i].y,&point[i].z); } for(int i=1;i<=n;i++){ edge[i][i]=0; for(int j=i+1;j<=n;j++){ double dis=sqrt(pow((point[i].x-point[j].x)*1.0,2)+pow((point[i].y-point[j].y)*1.0,2)); edge[i][j]=edge[j][i]=dis; } } prim(1); double r=-1; for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++)if(i!=j){ if(used[i][j]){ r=(r>(point[i].z+point[j].z)*1.0/(sum-edge[i][j])?r:(point[i].z+point[j].z)*1.0/(sum-edge[i][j])); }else if(!used[i][j]){ r=(r>(point[i].z+point[j].z)*1.0/(sum-Max[i][j])?r:(point[i].z+point[j].z)*1.0/(sum-Max[i][j])); } } } printf("%.2lf\n",r); } return 0;}