2011.07.18

POJ 1679 The Unique MST

题意:判断最小生成树的是否唯一

    哎模板悲催的只有优先队列啊…然后就比较麻烦了,因为第一个入队列的点有相等的边会没法正确判断,我的解决方法很搓,就是找一条没有相等边的点当第一个点,万幸数据弱过了= =

    模板上加了before[ ]记录每个点是被谁更新过,在遇到到树距离同样最短的边就判not unique……

#include<cstring>
#include<algorithm>
#include<queue>
#include<fstream>
#include<cstdio>
#define MAXE 10000
#define MAXV 105
using namespace std;
const int INF=(int)1<<31-1;
typedef pair<int,int> pii;
int t,m,n,V,E,pos=0,head[MAXV],num[MAXV],bian[MAXV];
int ans,d[MAXV];
struct Edge
{
    int w,next,v,u;
}node[MAXE*2];
//无向图邻接表
void add(int u,int v,int w)
{
    if(u==v) return;                  //如果指向自己的边则舍去
    node[pos].u=u;
	node[pos].v=v;
    node[pos].w=w;
    node[pos].next=head[u];
    head[u]=pos++;
	bian[u]++;
}
//S为起点标号
void prim(int b)
{
    bool done[MAXV];
    memset(done,0,sizeof(done));
	int bf[MAXV];memset(bf,0,sizeof(bf));
    priority_queue<pii,vector<pii>,greater<pii> > q;
    q.push(make_pair(d[b],b));
    while(!q.empty()){
        pii u=q.top();q.pop();
        int x=u.second;
		if(done[x]) continue;
        done[x]=true;
        for(int i=head[x];i!=-1;i=node[i].next)       //松弛操作
            if(!done[node[i].v] && d[node[i].v]>node[i].w){
                d[node[i].v]=node[i].w;
                q.push(make_pair(d[node[i].v],node[i].v));
				num[node[i].v]=0;bf[node[i].v]=x;
            }
			else if(bf[node[i].v]!=x && !done[node[i].v] && d[node[i].v]==node[i].w)
				num[node[i].v]=1;
    }
    return ;
}
int main()
{
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    int t=0,u,v,w,i,j;
	bool can[100000];
    scanf("%d",&t);
    while(t--){
        pos=1;
        memset(head,-1,sizeof(head));
		memset(num,0,sizeof(num));
		memset(bian,0,sizeof(bian));
        scanf("%d%d",&V,&E);
		if(E==0) {printf("0\n");continue;}
        while(E--){
                scanf("%d%d%d",&u,&v,&w);
                add(u,v,w);
                add(v,u,w);
        }
        for(i=1;i<=V;i++) d[i]=INF;
		v=u;
        for(i=1;i<=V;i++)if(bian[i]==1){v=i;break;}
		for(i=1;i<=V;i++){
			memset(can,0,sizeof(can));
			for(j=head[i];j!=-1;j=node[j].next)
				if(!can[node[j].w]) can[node[j].w]=1;
				else break;
				if(j==-1){ v=i;break;}
		}
		d[v]=0;
		prim(v);
        for(i=1;i<=V;i++){
			ans+=d[i];
			if(num[i]) break;
		}
        if(i>V) printf("%d\n",ans);
		else printf("Not Unique!\n");
        ans=0;
    }
    return 0;
}


    嗯,对prim的理解有更多一点,哎,你还差得远呢,加油啊少年~这么说突然想到昨天学弟问我是不是有被叫过少年,哈哈~说起来,都是当学姐的人了…我真沧桑= =


POJ 2728

    题意:求最优比率生成树一个带权完全图,每条边都有自己的花费值cost[i]和收益值benifit[i],如果用x[i]来代表一条边取或不取(0/1问题),那么求一个生成树。要求:r=(∑cost[i]*x[i] ) / (∑benifit[i]*x[i] )最小。

    本质上是0/1分数规划思想,对于上式可以变形为 z(r)=∑cost[i]*x[i] -r*∑benifit[i]*x[i]。而z(r)=0为我们所求。这里有个有意思的结论:z(r)为单调递减函数,因此是线性的。于是"我们可以兴高采烈地把z(r)看做以 cost[i]-r*benifit[i] 为边权的最小生成树的总权值"。抄过来三鲜的优美简明的结论~

    1.  z单调递减

  证明: 因为cost为正数, 所以z随l的减小而增大.

    2.  z( max(r) ) = 0

  证明: 若z( max(r) ) < 0, ∑(benifit[i] * x[i]) - max(r) * ∑(cost[i] * x[i]) < 0, 可化为 max(r) < max(r). 矛盾;

        若z( max(r) ) >= 0, 根据性质1, 当z = 0 时r最大. 

    确定r有两种方法,二分和迭代(Dinkelbach)。二分方法上界用Σ(benifit[i]*x[i])/min(cost[i])确定,这是个不可能取到的足够大的值。Dinkelbach效率更高。

复杂度:时间 O( O(MST) * log max(r) );空间 O( O(MST) )

要是你不幸的忘掉了传送门:http://hi.baidu.com/zzningxp/blog/item/b2d1b4ec1f8bbc2262d09fc9.html

Dinkelbach详解:http://s99f.blog.163.com/blog/static/3511836520094291100110/

#include<fstream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#define MAXE 10000
#define MAXV 1001
using namespace std;
const double inf=9999999;
double b[MAXV][MAXV],d[MAXV];                                              //b:benifit;c=cost;
int V,c[MAXV][MAXV],x[MAXV],y[MAXV],z[MAXV];                               
double tmp,ans;
double dis(int i,int j)
{
    return sqrt((double)(x[i]-x[j])*(x[i]-x[j])+(double)(y[i]-y[j])*(y[i]-y[j]));
}
double prim(double now)
{
    bool done[MAXV];
    int pre[MAXV],t,i,j;
    double mini[MAXV],mm,C,B;
    memset(done,0,sizeof(done));
    d[1]=0;done[1]=1;
    for(i=2;i<=V;i++){pre[i]=1;d[i]=c[1][i]-b[1][i]*now;}
    for(int i=1;i<=V;i++){
        mm=inf;
        for(int j=1;j<=V;j++)
            if(!done[j] && d[j]<mm){
                mm=d[j];t=j;
            }
        C+=c[pre[t]][t];
        B+=b[pre[t]][t];
        done[t]=1;
        for(int j=1;j<=V;j++){
            if(d[j]>c[t][j]-b[t][j]*now){                        //优先队列优化的prim会超时啊喂…
                d[j]=c[t][j]-b[t][j]*now;
                pre[j]=t;
            }
        }
    }
    return C/B;
}int main()
{
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    int i,j,k;
    while(scanf("%d",&V)!=EOF,V){
        int n=V;
        for(i=1;i<=n;i++)
            scanf("%d%d%d",&x[i],&y[i],&z[i]);
        for(i=1;i<=V;i++)
            for(j=1;j<=V;j++){
                b[i][j]=b[j][i]=dis(i,j);
                c[i][j]=c[j][i]=abs(z[i]-z[j]);
            }
        while(1){
            tmp=prim(ans);
            if(fabs(ans-tmp)<0.000001) break;
            else ans=tmp;
        }
        printf("%.3lf\n",ans);
    }
    return 0;
}

要是不是完全图会怎样呢?

嗷呜……让我做梦在去考虑吧……这种事……

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值