2011.07.18

来源:互联网 发布:p2p理财安全吗 知乎 编辑:程序博客网 时间:2024/05/01 17:19

POJ 1679 The Unique MST

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

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

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

#include<cstring>#include<algorithm>#include<queue>#include<fstream>#include<cstdio>#define MAXE 10000#define MAXV 105using 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 1001using 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;}

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

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

原创粉丝点击