2011.08.15

POJ  2186  Popular Cows

        题意:牛儿们想当最受欢迎的牛,每只牛有一些喜欢的牛,而规定受欢迎是有传递性的,如A喜欢B,B喜欢C,则A也喜欢C。问有几只牛儿受到所有牛的欢迎。

        解题:果的强连通分量,缩点后查看每个点的出度,出度为0的则是受欢迎的牛儿的集合。注意图可能不连通,故出度为0的点不一定受到所有牛儿的欢迎,这种情况则输出0,否则输出受欢迎的牛儿的集合元素个数。

#include<cstdio>
#include<cstring>
#include<fstream>
#define ME 50005
#define MV 10005
using namespace std;
struct edge
{
    int v,next;
}e[ME];
const int inf=1<<30;
int n,m,Index,cnt,top,pos,ans,h[MV],d[MV],low[MV],s[MV],f[MV];
int num[MV],chudu[MV],root;
bool done[MV],instack[MV];
void add(int u,int v)
{
    e[pos].v=v;
    e[pos].next=h[u];
    h[u]=pos++;
}
void tarjan(int u)
{
    int i,x,v;
    d[u]=low[u]=Index++;
    s[++top]=u;
    instack[u]=1;
    for(i=h[u];i!=-1;i=e[i].next){
        v=e[i].v;
        if(d[v]==-1){         //若v还没入栈处理过,dfs
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(instack[v])   //若在栈里没处理
            low[u]=min(low[u],d[v]);
    }
    if(d[u]==low[u]){        //找到一个极大强连通分量
        cnt++;
        while(top){
            x=s[top--];
            instack[x]=0;
            f[x]=cnt;  //利用并查集缩点
            num[f[x]]++;  //此题需要记录每个极大连通分量的点的个数
            if(x==u) break;
        }
    }
}
int main()
{
    int i,j,u,v;
    //freopen("in","r",stdin);
    //freopen("out","w",stdout);
    scanf("%d%d",&n,&m);
    memset(h,-1,sizeof(h));
    memset(d,-1,sizeof(d));
    while(m--){
        scanf("%d%d",&u,&v);
        add(u,v);
    }
    for(i=1;i<=n;i++)if(d[i]==-1)
        tarjan(i);
    for(i=1;i<=n;i++)for(j=h[i];j!=-1;j=e[j].next)    //计算出度
       if(f[e[j].v]!=f[i]) chudu[f[i]]++;
    for(i=1;i<=cnt;i++) if(chudu[i]==0){     
        root++;
        ans=num[i];
    }
    if(root>1 || root==0)
        printf("0\n");
    else printf("%d\n",ans);
    return 0;
}

tarjan求解强连通分量

       无敌链接: http://www.cnblogs.com/liyongmou/archive/2010/08/14/1799436.html


POJ  1904  King's Quest

        题意:国王的n哥王子喜欢n个妹纸中的某一些,给出每个王子喜欢的妹纸,和一份王子全娶到喜欢的女孩的完美匹配方案。要求每个王子可以选择哪些女孩可以让剩下的每个王子依旧能够选择到自己喜欢的一个女孩。题意真坑,半天看懂…还有神马破王国啊~妹纸不喜欢那些王子就喜欢邻家哥哥肿么办~结婚了等年老珠黄被王子踢出来还连房都分不到一间诶有木有~

        解题:嗯…开始想到了强连通分量,可是方向呢?全部去掉咩?太笨了吧…郁闷半天,discuss里各种嚎啕…索性直接看题解,喵了个咪,用完美匹配方案当妹纸到王子的边是多有意思,然后属于同一个强联通分量的点便是可以更改匹配的点。嗯,很有趣的题…二分图的背景却不使用匹配的算法解的,又贯穿了匹配的思想,很有趣啊~早上学了Tarjan后无压力的超模版飘过……

详细的这篇很不错:http://www.cnblogs.com/zxndgv/archive/2011/08/06/2129333.html

#include<cstdio>
#include<cstring>
#include<algorithm>
#define MV 4010
#define ME 205000
using namespace std;
const int inf=1<<30;
struct edge
{
    int v,next;
}e[ME];
int n,m,Index,cnt,top,pos,h[MV],d[MV],low[MV],s[MV],f[MV];
bool done[MV],instack[MV];
bool g[MV][MV];
int ans[MV][MV],p[MV];
void init()
{
    memset(h,-1,sizeof(h));
    memset(d,-1,sizeof(d));
    memset(g,0,sizeof(g));
    memset(p,0,sizeof(p));
    pos=1;
}
void add(int u,int v)
{
    e[pos].v=v;
    e[pos].next=h[u];
    h[u]=pos++;
}
void build_graph()
{
    int i,v,m;
    for(i=1;i<=n;i++){
        scanf("%d",&m);
        while(m--){
            scanf("%d",&v);
            add(i,v+n);
        }
    }
    for(i=1;i<=n;i++){
        scanf("%d",&v);
        add(v+n,i);
     //   ans[i][p[i]++]=v+n;
    }
}
void tarjan(int u)
{
    int i,x,v;
    d[u]=low[u]=Index++;
    s[++top]=u;
    instack[u]=1;
    for(i=h[u];i!=-1;i=e[i].next){
        v=e[i].v;
        if(d[v]==-1){         //若v还没入栈处理过,dfs
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(instack[v])   //若在栈里没处理
            low[u]=min(low[u],d[v]);
    }
    if(d[u]==low[u]){        //找到一个极大强联通分量
        cnt++;
        while(top){
            x=s[top--];
            instack[x]=0;
            f[x]=cnt;  //利用并查集缩点
            if(x==u) break;
        }
    }
}
int main()
{
    int i,j;
    while(scanf("%d",&n)!=EOF){
        init();
        build_graph();
        for(i=1;i<=n;i++)if(d[i]==-1){
            tarjan(i);
        }
        for(i=1;i<=n;i++)
            for(j=h[i];j!=-1;j=e[j].next)if(f[e[j].v]==f[i])
                ans[i][p[i]++]=e[j].v;
        for(i=1;i<=n;i++){
            sort(&ans[i][0],&ans[i][p[i]]);
            printf("%d ",p[i]);
            for(j=0;j<p[i]-1;j++)
                if(ans[i][j]!=ans[i][j+1])
                    printf("%d ",ans[i][j]-n);
            printf("%d\n",ans[i][p[i]-1]-n);
        }
        printf("\n");
    }
    return 0;
}


POJ  2195  Going Home

        题意:n*m的点阵上有一些人和相同数量的房子,人在图上横向或纵向移动一个格的代价是1,求让这些人各自走到一间不同的房子的最小代价。

        解题:果的最有匹配模板题。把最大权值变成最小权值的最有匹配方法是,把权值改为一个足够大的数减掉当前权值,计算完当前最大权值的最有匹配后再用大数*点数-最有匹配权值和。感谢梁神的指导,膜拜DD的n^3神模板,然后自己回到了不懂KM的时候= =

        注意最正确解最小权匹配的办法是用一个很大的数-当前边权值,而不是直接对边权取反(这样只能处理左右点相等的完全二分图,即K(n, n)

#include<cstdio>
#include<cstring>
#include<fstream>
#define MV 405
using namespace std;
const int inf=1<<30;
int m,n,N,pos,ans,p1,p2;
int x1[MV],y1[MV],x2[MV],y2[MV];
int visx[MV],visy[MV],linkx[MV],linky[MV],w[MV][MV],lx[MV],ly[MV],slack[MV];
int abs(int a)
{
    if(a<0) return -a;
    return a;
}
bool find(int x){
	visx[x]=true;
	for(int y=0;y<N;++y){
		if(visy[y])continue;
		int t=lx[x]+ly[y]-w[x][y];
		if(t==0){
			visy[y]=true;
			if(linky[y]==-1||find(linky[y])){
				linky[y]=x;
				return true;
			}
		}
		else{
			if(slack[y]>t)
				slack[y]=t;
		}
	}
	return false;
}
void KM(){
	memset(linky,-1,sizeof(linky));
	memset(lx,0,sizeof(lx));
	memset(ly,0,sizeof(ly));
	for(int i=0;i<N;++i)
		for(int j=0;j<N;++j)
			if(w[i][j]>lx[i])
				lx[i]=w[i][j];
	for(int x=0;x<N;++x){
		for(int i=0;i<N;++i)
			slack[i]=inf;
		for(;;){
			memset(visx,0,sizeof(visx));
			memset(visy,0,sizeof(visy));
			if(find(x))break;
			int d=inf;
			for(int i=0;i<N;++i){
				if(!visy[i])
					if(d>slack[i])
						d=slack[i];
			}
			for(int i=0;i<N;++i){
				if(visx[i])
					lx[i]-=d;
			}
			for(int i=0;i<N;++i){
				if(visy[i])
					ly[i]+=d;
				else
					slack[i]-=d;
			}
		}
	}
}
int main()
{
    char c;
    int i,j;
    while(EOF!=scanf("%d%d",&n,&m)){
        if(m==0 && n==0) break;
        ans=p1=p2=pos=0;
        for(i=1;i<=n;i++)for(j=0;j<=m;j++){
            scanf("%c",&c);
            if(c=='m'){x1[p1]=i;y1[p1++]=j;}
            else if(c=='H'){x2[p2]=i;y2[p2++]=j;}
        }
        for(i=0;i<p1;i++) for(j=0;j<p2;j++)
            w[i][j]=200-(abs(x1[i]-x2[j])+abs(y1[i]-y2[j]));
        N=p1;
        KM();
        for(i=0;i<N;i++)
            ans+=w[linky[i]][i];
        printf("%d\n",200*N-ans);
    }
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值