2015-2016 下半学期 第十周 训练

1、hdu4612

题意:

无向图连一条边后桥边数量最少是多少。

思路:

先将无向图缩点形成一棵树,再两次dfs取直径,在直径端点连边,这样可以使桥边减少的最多。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <stack>
#include <queue>
using namespace std;
const int maxn=200000+100;

struct Edge
{
    int v, nxt;
} edge[3000010];

int n, m;
int pre[maxn], low[maxn], dfn, head[maxn], tot;
int belong[maxn], block, bridge;
stack<int> S;
bool ins[maxn];

void init()
{
    memset(head, -1, sizeof head);
    memset(pre, 0, sizeof pre);
    memset(low, 0, sizeof low);
    memset(ins, 0, sizeof ins);
    memset(belong, 0, sizeof belong);
    dfn=tot=block=bridge=0;
}

void AddEdge(int u, int v)
{
    edge[tot].v=v;
    edge[tot].nxt=head[u];
    head[u]=tot++;
}
void Tarjan(int u, int fa)
{
    pre[u]=low[u]=++dfn;
    S.push(u);
    ins[u]=true;
    for(int i=head[u]; ~i; i=edge[i].nxt)
    {
        if(i==(fa^1))continue ;
        int v=edge[i].v;
        if(!ins[v])
        {
            Tarjan(v, i);
            low[u]=min(low[u], low[v]);
            if(low[v]>pre[u])
                bridge++;
        }
        else low[u]=min(low[u], pre[v]);
    }
    if(low[u]==pre[u])
    {
        block++;
        while(true)
        {
            int x=S.top();
            S.pop();
            ins[x]=false;
            belong[x]=block;
            if(x==u)break;
        }
    }
}

vector<int> G[maxn];
int d[maxn];
bool vis[maxn];
queue<int> q;
int bfs(int s, int& ans)
{
    memset(d, 0, sizeof d);
    memset(vis, 0, sizeof vis);
    int t=s, head=0, tail=0;
    q.push(s);
    vis[s]=true;
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=0; i<G[u].size(); i++)
        {
            int v=G[u][i];
            if(!vis[v])
            {
                vis[v]=true;
                d[v]=d[u]+1;
                if(d[v]>ans)
                    ans=d[t=v];
                q.push(v);
            }
        }
    }
    return t;
}

int main()
{
    while(~scanf("%d%d", &n, &m) && n+m)
    {
        init();
        while(m--)
        {
            int u, v;
            scanf("%d%d", &u, &v);
            AddEdge(u, v);
            AddEdge(v, u);
        }
        for(int i=1; i<=n; i++)
            if(!pre[i])
                Tarjan(i, -1);
        for(int i=0; i<=n; i++)
            G[i].clear();
        for(int u=1; u<=n; u++)
            for(int i=head[u]; ~i; i=edge[i].nxt)
            {
                int v=edge[i].v;
                if(belong[u]!=belong[v])
                    G[belong[u]].push_back(belong[v]);
            }
        int ans=0;
        int key=bfs(1, ans);
        bfs(key, ans);
        printf("%d\n", bridge-ans);
    }
    return 0;
}

2、 去掉两个点,问可以最多留下多少个连通分支。

思路:n^2枚举

代码:

#include<cstdio>
#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm>
#define MAX_N 5555
using namespace std;

vector<int> G[MAX_N];
bool vis[MAX_N];
int dfn[MAX_N],low[MAX_N],ind=0;

int cut[MAX_N];

int node;

void Tarjan(int u,int p){
    int child=0;
    dfn[u]=low[u]=++ind;
    vis[u]=1;
    for(int i=0;i<G[u].size();i++){
        int v=G[u][i];
        if(v==p||v==node)continue;
        if(!vis[v]){
            Tarjan(v,u);
            low[u]=min(low[v],low[u]);
            child++;
            if((p==-1&&child>1)||(p!=-1&&low[v]>=dfn[u]))
                cut[u]++;
        }
        else
            low[u]=min(dfn[v],low[u]);
    }
}

int n,m;

void init(){
    for(int i=0;i<=n;i++)G[i].clear();
    ind=0;
    memset(vis,0,sizeof(vis));
    memset(cut,0,sizeof(cut));
}

bool used[MAX_N];
int cu;
void dfs(int u,int p){
    if(u==p||used[u]||u==node||u==cu)return;
    used[u]=1;
    for(int i=0;i<G[u].size();i++)dfs(G[u][i],u);
}

int main(){
    while(scanf("%d%d",&n,&m)==2){
        int stab=1;
        init();
        int u,v;
        for(int i=0;i<m;i++) {
            scanf("%d%d", &u, &v);
            G[u].push_back(v);
            G[v].push_back(u);
        }
        for(int i=0;i<n;i++){
            node=i;
            memset(vis,0,sizeof(vis));
            ind=0;
            memset(cut,0,sizeof(cut));
            for(int j=0;j<n;j++)
                if((!vis[j])&&j!=node)
                    Tarjan(j,-1);
            int maxC=0;
            for(int j=0;j<n;j++)
                if(j!=node&&cut[j]>=maxC){
                    cu=j;
                    maxC=cut[j];
                }
            int ans=0;
            memset(used,0,sizeof(used));
            for(int j=0;j<n;j++)
                if((!used[j])&&j!=node&&j!=cu){
                    dfs(j,-1);
                    ans++;
                }
            stab=max(stab,ans);
        }
        printf("%d\n",stab);
    }

    return 0;
}

3、poj3694

题意:

思路:缩点后形成一棵树,加边后成环,环内所有的边都会变成非桥边 标记去重 减去即可。

代码:

<span style="font-family:Verdana, Geneva, Arial, Helvetica, sans-serif;">#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;

const int M=100005;

struct Eage                ///邻接表建图
{
    int v;
    int next;
    int vs;
};

Eage eage[M*4];
int low[M],dfn[M],father[M];  ///father[i]为i的父亲节点
int flag[M],vs[M],head[M];    ///falg[i]为i和i的父亲节点的边
int dfs_cut,ans,k;

void Init()
{
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(flag,0,sizeof(flag));
    memset(vs,0,sizeof(vs));
    memset(head,-1,sizeof(head));
    dfs_cut=ans=k=0;
}

void add(int u,int v)
{
    eage[k].v=v;
    eage[k].vs=0;
    eage[k].next=head[u];
    head[u]=k++;
}

void dfs(int u)                     ///求桥
{
    vs[u]=1;
    dfn[u]=low[u]=++dfs_cut;
    for (int i=head[u];i!=-1;i=eage[i].next)
    {
        if (!eage[i].vs)
        {
            eage[i].vs=eage[i^1].vs=1;
            int v=eage[i].v;
            if (!vs[v])
            {
                father[v]=u;
                dfs(v);
                low[u]=min(low[u],low[v]);
                if (dfn[u]<low[v])
                {
                    ans++;
                    flag[v]=1;
                }
            }
            else low[u]=min(low[u],dfn[v]);
        }
    }
}

void Lca(int u,int v)
{
    if (dfn[u]<dfn[v])
    {
        u^=v;
        v^=u;
        u^=v;
    }
    while (dfn[u]>dfn[v])
    {
        if (flag[u]) ans--;
        flag[u]=0;
        u=father[u];
    }
    while (u!=v)
    {
        if (flag[v]) ans--;
        if (flag[u]) ans--;
        flag[u]=0;
        flag[v]=0;
        v=father[v];
        u=father[u];
    }
}

int main()
{
  int n,m,cut=0;
    while (cin>>n>>m)
    {
        if (!n&&!m) return 0;
        Init();
        for (int i=1;i<=n;i++) father[i]=i;
        int u,v;
        while (m--)
        {
            cin>>u>>v;
            add(u,v);
            add(v,u);
        }
        dfs(1);
        cout<<"Case "<<++cut<<":"<<endl;
        int q;
        cin>>q;
        while (q--)
        {
            cin>>u>>v;
            Lca(u,v);
            cout<<ans<<endl;
        }
        cout<<endl;
    }
    return 0;
}
</span>

4、poj1904

题意:

思路:奇怪的二分图+强连通分量 不会。

代码:

<span style="font-family:Verdana, Geneva, Arial, Helvetica, sans-serif;">#include<cstdio>
#include<cstring>
#include<climits>
#include<iostream>
#include<algorithm>
 
#define N 2010
 
namespace Fio{
    inline int getc(){
#ifdef ONLINE_JUDGE
        static const int L=1<<15;
#else
        static const int L=1<<1;
#endif
        static char buf[L],*S=buf,*T=buf;
        if(S==T){T=(S=buf)+fread(buf,1,L,stdin);if(S==T)return EOF;}
        return*S++;
    }
    inline bool digit(int c){return c>='0'&&c<='9';}
    template<typename T>inline void Get(T&x){
        int c;while(!digit(c=getc()));x=c-'0';while(digit(c=getc()))x=(x<<1)+(x<<3)+c-'0';
    }
    char buf[5000000],*o=buf;
    inline void putc(char c){*o++=c;}
    template<typename T>inline void print(T x){
        static int stk[100];int top=0;
        for(;x;x/=10)stk[++top]=x%10;for(int i=top;i>=1;--i)*o++='0'+stk[i];
    }
    inline void Final(){fwrite(buf,1,o-buf,stdout);}
}
 
int head[4010],next[2010*2010],end[2010*2010];
inline void addedge(int a,int b){static int q=1;end[q]=b,next[q]=head[a],head[a]=q++;}
 
int G[2010][2010],dfn[4010],low[4010],tclock,stk[4010],bel[4010],cnt,top;bool instk[4010];
 
void dfs(int x){
    dfn[x]=low[x]=++tclock;stk[++top]=x,instk[x]=1;
    for(int j=head[x];j;j=next[j]){
        if(!dfn[end[j]])dfs(end[j]),low[x]=std::min(low[x],low[end[j]]);
        else if(instk[end[j]])low[x]=std::min(low[x],dfn[end[j]]);
    }
    if(dfn[x]==low[x]){
        ++cnt;
        while(1){
            int i=stk[top--];instk[i]=0;
            bel[i]=cnt;
            if(i==x)break;
        }
    }
}
 
int seq[2010],id;
 
int main(){
    int n;Fio::Get(n);register int i,j;
    int t,x;for(i=1;i<=n;++i){Fio::Get(t);while(t--)Fio::Get(x),G[i][x]=1,addedge(i,n+x);}
 
    for(i=1;i<=n;++i)Fio::Get(x),addedge(n+x,i);
 
    for(i=1;i<=2*n;++i)if(!dfn[i])dfs(i);
 
    for(i=1;i<=n;++i){
        for(id=0,j=1;j<=n;++j)if(bel[i]==bel[n+j]&&G[i][j])seq[++id]=j;
        Fio::print(id);
        for(j=1;j<=id;++j)Fio::putc(' '),Fio::print(seq[j]);
        Fio::putc('\n');
        //printf("%d",id);
        //for(j=1;j<=id;++j)printf(" %d",seq[j]);
        //puts("");
    }
 
    Fio::Final();
 
#ifndef ONLINE_JUDGE
    system("pause");
#endif
    return 0;
}</span>

你可能感兴趣的:(2015-2016 下半学期 第十周 训练)