BUPT Spring Ranking Contest For 13 Round #4 graph

比赛地址:点击打开链接


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;}

 
 




你可能感兴趣的:(图论)