E81086713E446D36F62B2AA2A3502B5EB155

Java杂家

最小割等于最大流

BlogJava 首页 新随笔 联系 聚合 管理
  38 Posts :: 0 Stories :: 126 Comments :: 0 Trackbacks

置顶随笔 #

原题:

Command Network

Description

After a long lasting war on words, a war on arms finally breaks out between littleken’s and KnuthOcean’s kingdoms. A sudden and violent assault by KnuthOcean’s force has rendered a total failure of littleken’s command network. A provisional network must be built immediately. littleken orders snoopy to take charge of the project.

With the situation studied to every detail, snoopy believes that the most urgent point is to enable littenken’s commands to reach every disconnected node in the destroyed network and decides on a plan to build a unidirectional communication network. The nodes are distributed on a plane. If littleken’s commands are to be able to be delivered directly from a node A to another node B, a wire will have to be built along the straight line segment connecting the two nodes. Since it’s in wartime, not between all pairs of nodes can wires be built. snoopy wants the plan to require the shortest total length of wires so that the construction can be done very soon.

Input

The input contains several test cases. Each test case starts with a line containing two integer N (N ≤ 100), the number of nodes in the destroyed network, and M (M ≤ 104), the number of pairs of nodes between which a wire can be built. The next N lines each contain an ordered pair xi and yi, giving the Cartesian coordinates of the nodes. Then follow M lines each containing two integers i and j between 1 and N (inclusive) meaning a wire can be built between node i and node j for unidirectional command delivery from the former to the latter. littleken’s headquarter is always located at node 1. Process to end of file.

Output

For each test case, output exactly one line containing the shortest total length of wires to two digits past the decimal point. In the cases that such a network does not exist, just output ‘poor snoopy’.


一开始没仔细读题,一看以为是最小生成树呢,结果Krusal算法上去WA了,Prim算法也WA,修修改改一直WA,第二天发现本题是要在有向图上面构造最小树形图。

按照著名的Zhu-Liu算法,仔细实现了一边,终于AC了。
按照我的理解总结下该算法,该算法对每个结点,除根节点外寻找最小入边,
1)如果这些入边不构成环,那么容易证明这些边构成最小树形图。
证明:设加上根节点r一共N个点,那么一共有N-1条边,证明从r能到每个点,若存在一点v,使得从r到v没有路径,那么,假设从v反向回退必然构成环,因为每个点除了r都有入边,如果不构成环,该路径可以无穷大。
2)如果存在环,我们把环收缩成一个点,更新相应的入边和出边,得到新的图G',使得原问题在G'中等效:
怎么收缩呢?
假设我们把环收缩成环上的任意一点v,所有进环的边和出环的边自动变成v的边(如果已有,取长度最短的),其余点标记为删除,更新不在环上的所有点进入该环的长度cost为cost-cost(prev[x],x);其中点x为进入环的边在环上的端点。出边保持不变。

这里为什么这么更新?因为这样更新使得我们的算法在新图上是等效的。任何环的解决后意味着在新图里面得为改收缩后的点寻找新的入边,而实际的花费应该是新的入边减去原有的入边长度,我们的算法在找到环的时候就把环上所有的边的长度计算在花费内了.而对出边是没有影响的。


到这算法的框架基本出来了。当为某点没找到入边的时候,意味着无解。为了加快无解的侦测,我们先运行一遍DFS搜索,假如从根节点出发,可触及的节点数小于N-1(不含r)则意味着无解。反之,肯定有解。
为什么?
因为如果可触及数小于N-1,意味着某点是不可触及的,也就是原图不是弱连通。对该点来说不存在从r到它的路径。反之,从r到某点都有一条路径,沿着该路径就能找到结点的入边。


第二个问题是,如何快速侦测环呢?
我使用了一个不相交集。回忆Krusal的算法实现里面也是使用不相交集来避免找产生环的最小边。

下面是我的代码:

// 3164.cpp : Defines the entry point for the console application.
//

#include 
<iostream>
#include 
<cmath>


using namespace std;

typedef 
struct _Point
{

    
double x;
    
double y;

    
double distanceTo(const struct _Point& r)
    {
          
return sqrt((x-r.x)*(x-r.x)+(y-r.y)*(y-r.y));

    }

}Point;



const int MAX_V=100;
const int MAX_E=10000;
const double NO_EDGE=1.7976931348623158e+308;

Point vertexes[MAX_V]
={0};
int parents[MAX_V]={0};
int ranks[MAX_V]={0};
double G[MAX_V][MAX_V]={0};
bool visited[MAX_V]={0};
bool deleted[MAX_V]={0};
int prev[MAX_V]={0};

int nVertex=0;
int nEdge=0;




int u_find(int a)
{
    
if(parents[a]==a)return a;
    parents[a]
=u_find(parents[a]);
    
return parents[a];
}
void u_union(int a,int b)
{
    
int pa=u_find(a);
    
int pb=u_find(b);
    
if(ranks[pa]==ranks[pb])
    {
        ranks[pa]
++;
        parents[pb]
=pa;
    }
else if(ranks[pa]<ranks[pb])
    {
        parents[pa]
=pb;
    }
    
else
    {
        parents[pb]
=pa;
    }
}

void DFS(int v,int& c)
{

    visited[v]
=true;
    
for(int i=1;i<nVertex;i++)
    {
        
if(!visited[i]&&G[v][i]<NO_EDGE)
        {
            c
+=1;

            DFS(i,c);
        }

    }

}



void doCycle(int s,int t,double& cost)
{
    memset(visited,
0,sizeof(bool)*nVertex);
    
int i=s;
    
do
    {
        visited[i]
=true;
        cost
+=G[prev[i]-1][i];
        
//cout<<"from "<<(prev[i]-1)<<" to "<<i<<" (cycle)"<<" weight:"<<G[prev[i]-1][i]<<endl;
        i=prev[i]-1;

    }
while(i!=s);


    
do
    {
        

        
for(int k=0;k<nVertex;k++)
        {
            
if(!deleted[k]&&!visited[k])
            {
            
                
if(G[k][i]<NO_EDGE)
                {
                    
if(G[k][i]-G[prev[i]-1][i]<G[k][s])
                    {


                        G[k][s]
=G[k][i]-G[prev[i]-1][i];
                        
//cout<<"1.update ["<<k<<","<<s<<"] at "<<i<<" as "<<G[k][s]<<endl;
                    }
                    

                }
                
if(G[i][k]<NO_EDGE)
                {
                    
if(G[i][k]<G[s][k])
                    {

                        G[s][k]
=G[i][k];
                        
//cout<<"2.update ["<<s<<","<<k<<"] at "<<i<<" as "<<G[s][k]<<endl;
                    }
                    
                }
            }
        }


        
if(i!=s)
        {
            deleted[i]
=true;
            
//cout<<"mark "<<i<<" as deleted"<<endl;
        }
        i
=prev[i]-1;


    }
while(i!=s);



}




int main(void)
{



    
    
while(cin>>nVertex>>nEdge)
    {

        
int s,t;

        
int nv=0;
        
bool cycle=0;
        
double cost=0;
        memset(vertexes,
0,sizeof(vertexes));
        memset(visited,
0,sizeof(visited) );

        memset(deleted,
0,sizeof(deleted));
        memset(G,
0,sizeof(G));
        memset(prev,
0,sizeof(prev));


        memset(ranks,
0,sizeof(ranks));

        memset(parents,
0,sizeof(parents));

        
for(int i=0;i<nVertex;i++)
        {

            cin
>>vertexes[i].x>>vertexes[i].y;
            parents[i]
=i;
            
for(int j=0;j<nVertex;j++)
            {
                G[i][j]
=NO_EDGE;
            }

        }
        
for(int i=0;i<nEdge;i++)
        {

            cin
>>s>>t;
            
if(t==1||s==t)continue;
            G[s
-1][t-1]=vertexes[s-1].distanceTo(vertexes[t-1]);

        }



        DFS(
0,nv);
        
if(nv<nVertex-1)
        {

            cout
<<"poor snoopy"<<endl;
            
continue;
        }



        
do {
            cycle
=false;
            
for(int i=0;i<nVertex;i++){parents[i]=i;}
            memset(ranks,
0,sizeof(bool)*nVertex);
        

            
for (int i = 1; i < nVertex; i++) {
                
double minimum=NO_EDGE;
                
if(deleted[i])continue;
                
for(int k=0;k<nVertex;k++)
                {
                    
if(!deleted[k]&&minimum>G[k][i])
                    {
                        prev[i]
=k+1;
                        minimum
=G[k][i];
                    }
                }
                
if(minimum==NO_EDGE)
                {
                

                    
throw 1;
                }
                
if (u_find(prev[i]-1== u_find(i)) {
                    doCycle(prev[i]
-1,i, cost);
                    cycle 
= true;
                    
break;
                }
                
else
                {
                    u_union(i,prev[i]
-1);
                }

            }


        } 
while (cycle);

        
for (int i = 1; i < nVertex; i++) {
            
if(!deleted[i])
            {
                cost
+=G[prev[i]-1][i];
                
//cout<<"from "<<(prev[i]-1)<<" to "<<i<<" weight:"<<G[prev[i]-1][i]<<endl;
            }

        }

        printf(
"%.2f\n",cost);


    }


}



posted @ 2009-04-24 16:39 DoubleH 阅读(1005) | 评论 (0)编辑 收藏

   今天买了本《算法概论》影印注释版,仔细看了第一章,果然名不虚传,很是吸引人。
第一章的习题难度适中,这里抽出第35题来,这题是证明Wilson定理。
Wilson定理:
   N是一个素数当且仅当: (N-1)! ≡ -1(mod N)

证明:
  首先我们证明若N是素数,那么等式成立,对于N=2,这是很明显的。以下证明N>2 的情形。
  1)若N是素数,那么关于N同余的乘法群G={1,2,3....N-1}
    群G每个元素都有逆元,映射 f:a -> a^-1 ,f(a)=a^-1 是一个一一映射。现在,任取一个元素,它的逆元要么是其它一个元素,或者是它本身。 我们假设其中元素x的逆元是它本身,那么x*x ≡1(mod N) =>(x+1)*(x-1)=K*N,而N是素数,所以要么x=N-1,要么x=1。也就是说,除了这两个元素,其它的元素的逆元都是映射到别的元素的。而N>2,是奇数,所以元素共有N-1个,也就是偶数个元素。这样,除了1和N-1外剩余的N-3个元素刚好结成两两一对(x,y)(逆元是唯一的)使得f(x)=y=x^-1,也就是xy≡1(mod N).
现在把G的元素全部乘起来,让互为逆元的元素组成一对那么(N-1)!=1*(N-1)*(x1*y1)*(x2*y2)...(xk*yk)≡1*(N-1)*1*1...1 (mod N)≡-1(mod N).
    这样,我们证明了一个方向了,下面我们证明另一个方向

  2)若(N-1)! ≡ -1(mod N)成立,则N是素数,若不然,令 N是和数。则(N-1)!肯定整除N,因为N的每个因子p满足p>1,p<N。
   现在令(N-1)!=K1*N.又(N-1)! ≡ -1(mod N) => K1*N ≡ -1(mod N),由同余性质知,存在K2使得K1*N+1=K2*N,两边同时除以N得K1+1/N=K2.显然,这是不可能的,所以若(N-1)! ≡ -1(mod N)成立,则N是素数

证明完毕!

这里用群的概念能够简化一定的描述,其实可以完全不用群的概念的,只不过这样一来,描述更长点,繁琐点!





posted @ 2009-04-10 22:38 DoubleH 阅读(1091) | 评论 (0)编辑 收藏

2009年5月4日 #

问题:
有个链表(List),有N个元素,当N很大的时候,我们通常想分批处理该链表。假如每次处理M条(0<M<=N),那么需要处理几次才能处理完所有数据呢?

问题很简单,我们需要<N/M>次,这里我们用<>表示向上取整,[]表示向下取整,那么怎么来表示这个值呢?
我们可以证明:
<N/M>=[(N-1)/M]+1    (0<M<=N,M,N∈Z)

不失一般性,我们设N=Mk+r(0<=r<M),
1)当r>0时,

左边:<N/M>=<(Mk+r)/M>=<k+r/M>=k+<r/M>=k+1
右边:[(N-1)/M]+1=[(Mk+r-1)/M]+1=[k+(r-1)/M]+1=k+1+[(r-1)/M]=k+1
2)当r=0
左边:<N/M>=k
右边:[(N-1)/M]+1=[(Mk-1)/M]+1=[(M(k-1)+M-1)/M]+1=[k-1+(M-1)/M]+1=k+[(M-1)/M]=k

命题得证。

有了这个公式,我们在Java代码里可以这样计算:
int nn=(N-1)/+1
.


因为'/'是往下取整的。








posted @ 2009-05-04 11:45 DoubleH 阅读(1031) | 评论 (2)编辑 收藏

2009年4月24日 #

原题:

Command Network

Description

After a long lasting war on words, a war on arms finally breaks out between littleken’s and KnuthOcean’s kingdoms. A sudden and violent assault by KnuthOcean’s force has rendered a total failure of littleken’s command network. A provisional network must be built immediately. littleken orders snoopy to take charge of the project.

With the situation studied to every detail, snoopy believes that the most urgent point is to enable littenken’s commands to reach every disconnected node in the destroyed network and decides on a plan to build a unidirectional communication network. The nodes are distributed on a plane. If littleken’s commands are to be able to be delivered directly from a node A to another node B, a wire will have to be built along the straight line segment connecting the two nodes. Since it’s in wartime, not between all pairs of nodes can wires be built. snoopy wants the plan to require the shortest total length of wires so that the construction can be done very soon.

Input

The input contains several test cases. Each test case starts with a line containing two integer N (N ≤ 100), the number of nodes in the destroyed network, and M (M ≤ 104), the number of pairs of nodes between which a wire can be built. The next N lines each contain an ordered pair xi and yi, giving the Cartesian coordinates of the nodes. Then follow M lines each containing two integers i and j between 1 and N (inclusive) meaning a wire can be built between node i and node j for unidirectional command delivery from the former to the latter. littleken’s headquarter is always located at node 1. Process to end of file.

Output

For each test case, output exactly one line containing the shortest total length of wires to two digits past the decimal point. In the cases that such a network does not exist, just output ‘poor snoopy’.


一开始没仔细读题,一看以为是最小生成树呢,结果Krusal算法上去WA了,Prim算法也WA,修修改改一直WA,第二天发现本题是要在有向图上面构造最小树形图。

按照著名的Zhu-Liu算法,仔细实现了一边,终于AC了。
按照我的理解总结下该算法,该算法对每个结点,除根节点外寻找最小入边,
1)如果这些入边不构成环,那么容易证明这些边构成最小树形图。
证明:设加上根节点r一共N个点,那么一共有N-1条边,证明从r能到每个点,若存在一点v,使得从r到v没有路径,那么,假设从v反向回退必然构成环,因为每个点除了r都有入边,如果不构成环,该路径可以无穷大。
2)如果存在环,我们把环收缩成一个点,更新相应的入边和出边,得到新的图G',使得原问题在G'中等效:
怎么收缩呢?
假设我们把环收缩成环上的任意一点v,所有进环的边和出环的边自动变成v的边(如果已有,取长度最短的),其余点标记为删除,更新不在环上的所有点进入该环的长度cost为cost-cost(prev[x],x);其中点x为进入环的边在环上的端点。出边保持不变。

这里为什么这么更新?因为这样更新使得我们的算法在新图上是等效的。任何环的解决后意味着在新图里面得为改收缩后的点寻找新的入边,而实际的花费应该是新的入边减去原有的入边长度,我们的算法在找到环的时候就把环上所有的边的长度计算在花费内了.而对出边是没有影响的。


到这算法的框架基本出来了。当为某点没找到入边的时候,意味着无解。为了加快无解的侦测,我们先运行一遍DFS搜索,假如从根节点出发,可触及的节点数小于N-1(不含r)则意味着无解。反之,肯定有解。
为什么?
因为如果可触及数小于N-1,意味着某点是不可触及的,也就是原图不是弱连通。对该点来说不存在从r到它的路径。反之,从r到某点都有一条路径,沿着该路径就能找到结点的入边。


第二个问题是,如何快速侦测环呢?
我使用了一个不相交集。回忆Krusal的算法实现里面也是使用不相交集来避免找产生环的最小边。

下面是我的代码:

// 3164.cpp : Defines the entry point for the console application.
//

#include 
<iostream>
#include 
<cmath>


using namespace std;

typedef 
struct _Point
{

    
double x;
    
double y;

    
double distanceTo(const struct _Point& r)
    {
          
return sqrt((x-r.x)*(x-r.x)+(y-r.y)*(y-r.y));

    }

}Point;



const int MAX_V=100;
const int MAX_E=10000;
const double NO_EDGE=1.7976931348623158e+308;

Point vertexes[MAX_V]
={0};
int parents[MAX_V]={0};
int ranks[MAX_V]={0};
double G[MAX_V][MAX_V]={0};
bool visited[MAX_V]={0};
bool deleted[MAX_V]={0};
int prev[MAX_V]={0};

int nVertex=0;
int nEdge=0;




int u_find(int a)
{
    
if(parents[a]==a)return a;
    parents[a]
=u_find(parents[a]);
    
return parents[a];
}
void u_union(int a,int b)
{
    
int pa=u_find(a);
    
int pb=u_find(b);
    
if(ranks[pa]==ranks[pb])
    {
        ranks[pa]
++;
        parents[pb]
=pa;
    }
else if(ranks[pa]<ranks[pb])
    {
        parents[pa]
=pb;
    }
    
else
    {
        parents[pb]
=pa;
    }
}

void DFS(int v,int& c)
{

    visited[v]
=true;
    
for(int i=1;i<nVertex;i++)
    {
        
if(!visited[i]&&G[v][i]<NO_EDGE)
        {
            c
+=1;

            DFS(i,c);
        }

    }

}



void doCycle(int s,int t,double& cost)
{
    memset(visited,
0,sizeof(bool)*nVertex);
    
int i=s;
    
do
    {
        visited[i]
=true;
        cost
+=G[prev[i]-1][i];
        
//cout<<"from "<<(prev[i]-1)<<" to "<<i<<" (cycle)"<<" weight:"<<G[prev[i]-1][i]<<endl;
        i=prev[i]-1;

    }
while(i!=s);


    
do
    {
        

        
for(int k=0;k<nVertex;k++)
        {
            
if(!deleted[k]&&!visited[k])
            {
            
                
if(G[k][i]<NO_EDGE)
                {
                    
if(G[k][i]-G[prev[i]-1][i]<G[k][s])
                    {


                        G[k][s]
=G[k][i]-G[prev[i]-1][i];
                        
//cout<<"1.update ["<<k<<","<<s<<"] at "<<i<<" as "<<G[k][s]<<endl;
                    }
                    

                }
                
if(G[i][k]<NO_EDGE)
                {
                    
if(G[i][k]<G[s][k])
                    {

                        G[s][k]
=G[i][k];
                        
//cout<<"2.update ["<<s<<","<<k<<"] at "<<i<<" as "<<G[s][k]<<endl;
                    }
                    
                }
            }
        }


        
if(i!=s)
        {
            deleted[i]
=true;
            
//cout<<"mark "<<i<<" as deleted"<<endl;
        }
        i
=prev[i]-1;


    }
while(i!=s);



}




int main(void)
{



    
    
while(cin>>nVertex>>nEdge)
    {

        
int s,t;

        
int nv=0;
        
bool cycle=0;
        
double cost=0;
        memset(vertexes,
0,sizeof(vertexes));
        memset(visited,
0,sizeof(visited) );

        memset(deleted,
0,sizeof(deleted));
        memset(G,
0,sizeof(G));
        memset(prev,
0,sizeof(prev));


        memset(ranks,
0,sizeof(ranks));

        memset(parents,
0,sizeof(parents));

        
for(int i=0;i<nVertex;i++)
        {

            cin
>>vertexes[i].x>>vertexes[i].y;
            parents[i]
=i;
            
for(int j=0;j<nVertex;j++)
            {
                G[i][j]
=NO_EDGE;
            }

        }
        
for(int i=0;i<nEdge;i++)
        {

            cin
>>s>>t;
            
if(t==1||s==t)continue;
            G[s
-1][t-1]=vertexes[s-1].distanceTo(vertexes[t-1]);

        }



        DFS(
0,nv);
        
if(nv<nVertex-1)
        {

            cout
<<"poor snoopy"<<endl;
            
continue;
        }



        
do {
            cycle
=false;
            
for(int i=0;i<nVertex;i++){parents[i]=i;}
            memset(ranks,
0,sizeof(bool)*nVertex);
        

            
for (int i = 1; i < nVertex; i++) {
                
double minimum=NO_EDGE;
                
if(deleted[i])continue;
                
for(int k=0;k<nVertex;k++)
                {
                    
if(!deleted[k]&&minimum>G[k][i])
                    {
                        prev[i]
=k+1;
                        minimum
=G[k][i];
                    }
                }
                
if(minimum==NO_EDGE)
                {
                

                    
throw 1;
                }
                
if (u_find(prev[i]-1== u_find(i)) {
                    doCycle(prev[i]
-1,i, cost);
                    cycle 
= true;
                    
break;
                }
                
else
                {
                    u_union(i,prev[i]
-1);
                }

            }


        } 
while (cycle);

        
for (int i = 1; i < nVertex; i++) {
            
if(!deleted[i])
            {
                cost
+=G[prev[i]-1][i];
                
//cout<<"from "<<(prev[i]-1)<<" to "<<i<<" weight:"<<G[prev[i]-1][i]<<endl;
            }

        }

        printf(
"%.2f\n",cost);


    }


}



posted @ 2009-04-24 16:39 DoubleH 阅读(1005) | 评论 (0)编辑 收藏

2009年4月10日 #

   今天买了本《算法概论》影印注释版,仔细看了第一章,果然名不虚传,很是吸引人。
第一章的习题难度适中,这里抽出第35题来,这题是证明Wilson定理。
Wilson定理:
   N是一个素数当且仅当: (N-1)! ≡ -1(mod N)

证明:
  首先我们证明若N是素数,那么等式成立,对于N=2,这是很明显的。以下证明N>2 的情形。
  1)若N是素数,那么关于N同余的乘法群G={1,2,3....N-1}
    群G每个元素都有逆元,映射 f:a -> a^-1 ,f(a)=a^-1 是一个一一映射。现在,任取一个元素,它的逆元要么是其它一个元素,或者是它本身。 我们假设其中元素x的逆元是它本身,那么x*x ≡1(mod N) =>(x+1)*(x-1)=K*N,而N是素数,所以要么x=N-1,要么x=1。也就是说,除了这两个元素,其它的元素的逆元都是映射到别的元素的。而N>2,是奇数,所以元素共有N-1个,也就是偶数个元素。这样,除了1和N-1外剩余的N-3个元素刚好结成两两一对(x,y)(逆元是唯一的)使得f(x)=y=x^-1,也就是xy≡1(mod N).
现在把G的元素全部乘起来,让互为逆元的元素组成一对那么(N-1)!=1*(N-1)*(x1*y1)*(x2*y2)...(xk*yk)≡1*(N-1)*1*1...1 (mod N)≡-1(mod N).
    这样,我们证明了一个方向了,下面我们证明另一个方向

  2)若(N-1)! ≡ -1(mod N)成立,则N是素数,若不然,令 N是和数。则(N-1)!肯定整除N,因为N的每个因子p满足p>1,p<N。
   现在令(N-1)!=K1*N.又(N-1)! ≡ -1(mod N) => K1*N ≡ -1(mod N),由同余性质知,存在K2使得K1*N+1=K2*N,两边同时除以N得K1+1/N=K2.显然,这是不可能的,所以若(N-1)! ≡ -1(mod N)成立,则N是素数

证明完毕!

这里用群的概念能够简化一定的描述,其实可以完全不用群的概念的,只不过这样一来,描述更长点,繁琐点!





posted @ 2009-04-10 22:38 DoubleH 阅读(1091) | 评论 (0)编辑 收藏

2009年1月17日 #





今天去网上看了一下09年的考研试题,看见该题目(图片):



先来定义结点(为了简便,省略set/get):
public class Node
{
 
public int data;
 
public Node link;
}
我能想到的两种解法,一个基于递归:

递归版的思路就是,基于当前结点,如果后一个是倒数第K-1,那么当前结点是所求,若不然,返回当前是倒数第几个。
public int printRKWithRecur(Node head,int k)
    {
        
if(k==0||head==null||head.link==null)return 0;
        
if(_recurFind(head.link,k)>=k)return 1;
        
return 0;
    }
    
private final int _recurFind(Node node, int k) {
        
if(node.link==null)
        {
            
return 1;
        }
        
int sRet=_recurFind(node.link,k);
        
if(sRet==k-1)
        {
            System.out.println(
"Got:"+node.data);
            
return k;
        }
        
return sRet+1;
    }


对每个结点,该算法都只访问一次,因此复杂度O(N).

第二解法,相对递归来说,这种方法可以算是消除递归版,而且从某种意义上来说比递归更高效,跟省空间,递归版实际上是把回溯的数据存在栈上,而版方法是自己存储,且利用数组实现一个循环队列,只存储K个元素。

public static class CycleIntQueue
    {
        
int[] datas;
        
int top=0;
        
int num=0;
        
public CycleIntQueue(int n)
        {
            datas
=new int[n];
        }
        
        
public void push(int i)
        {
            datas[(top
++)%datas.length]=i;
            num
++;
            
        }
        
public int numPushed()
        {
            
return num;
        }
        
        
        
public int getButtom()
        {
            
return datas[top%datas.length];
        }
    }
    
public int printRKWithCycleQueue(Node head,int k)
    {
        
if(k==0||head==null)return 0;
        CycleIntQueue queue
=new CycleIntQueue(k);
        Node cur
=head.link;
        
while(cur!=null)
        {
            queue.push(cur.data);
            cur
=cur.link;
        }
        
if(queue.numPushed()<k)return 0;
        
        System.out.println(
"Got:"+queue.getButtom());
        
return 1;
    }

本算法,都每个结点也只放一次,另外进行一次入队操作,该操作复杂度O(1),从而,整个算法复杂度仍是O(N).


posted @ 2009-01-17 13:56 DoubleH 阅读(1511) | 评论 (5)编辑 收藏

2009年1月6日 #


近日在CSDN上看到中软一道面试题,挺有意思的。
题目:一条小溪上7块石头,如图所示:

分别有六只青蛙:A,B,C,D,E,F。A,B,C三只蛙想去右岸,它们只会从左向右跳;D,E,F三只蛙想去左岸,它们只会从右向左跳。青蛙每次最多跳到自己前方第2块石头上。请问最少要跳几次所有青蛙上岸。写出步骤。

这个题是个路径搜索的问题,在解空间搜索所有的解,并找出最优的解法(即步骤最少的)。
那么怎么算是一个解呢?具体而言就是最后石头上没有青蛙了。



我们先给题目建模,7块石头,其上可以是没青蛙,可以有一只往左跳的青蛙,也可以有一只往右跳的青蛙。可以把这7块石头看成一个整体,来表示一个状态。这里我们把这7块石头看成一个数组,里面只能有0,1,2三种值,这样表示,那么初始时为:
1,1,1,0,2,2,2
我们把它再表示成一个数字,来表示状态值,这个值把这个数组按三进制拼成一个数字,我们用一个辅助函数来做这件事情:
private final int makeS() {
        
        
int r=0;
        
int p=1;
        
for(int i=0;i<7;i++)
        {
            r
+=p*states[i];
            p
*=3;
        }
        
return r;
    }

那么题目现在变成从状态111022转换成状态0000000,所需最少的步骤.

那么状态是怎样转换的呢?
很显然。,每次青蛙跳都会触发状态的转换,我们在每个状态时搜索每种可能的转换,我们记初始状态为S(S等于三进制111022)记要求解的值为OPT(S),假如可以转换到t1,t2,...tk.
那么,显然
OPT(S)=min(1+OPT(t1),1+OPT(t2),.,1+OPT(tk));

另外,由于最终状态为0,所以OPT(0)=0,就是说已经在最终状态了,就不需要一步就可以了。
有了上面这个等式,我们可以递归求解了,但是如果单纯的递归,会导致大量的重复计算,所以这里我们用备忘录的方法,记下已经求解出来的OPT(x),放在一个数组里,由于只有7块石头,所以最多我们需要3^7=2187个状态。我们用一个2187个元素的数组,  其中第i个元素表示OPT(i),初始化每个元素用-1表示还未求解。OPT(0) 可直接初始化为0.

到此我们还有一个问题,怎么能够在算法结束的时候打印出最优的步骤呢?按照这个步骤,我们可以重建出青蛙是如何在最优的情况下过河的。为此,我们可以再用一个步骤数组,每次在采取最优步骤的时候记录下来。

整个算法如下:
package test;

import java.util.Arrays;
/**
 *
 * @author Yovn
 *
 */
public class FrogJump {
   
    private int steps[];
    private int states[];
   
   
    private static class Step
    {
        int offset=-1;
        int jump;
        int jumpTo;
    }
   
   
    private Step jumps[];
    private int initS;
    public FrogJump()
    {
        steps=new int[81*27];
        states=new int[7];
        for(int i=0;i<3;i++)states[i]=1;
        for(int i=4;i<7;i++)states[i]=2;
        Arrays.fill(steps, -1);
        steps[0]=0;
        jumps=new Step[81*27];
        initS=makeS();
    }
   
    public int shortestSteps(int s)
    {
        if(steps[s]==-1)
        {

            int minStep=Integer.MAX_VALUE;
            Step oneStep=new Step();
            for(int i=0;i<7;i++)
            {
                if(states[i]==1)
                {
                    if(i>4)
                    {
                        states[i]=0;
                        minStep = recurFind(minStep,oneStep,i,7-i);
                        states[i]=1;
                    }
                    else
                    {
                        if(states[i+1]==0)
                        {
                            states[i]=0;
                            states[i+1]=1;
                            minStep = recurFind(minStep,oneStep,i,1);
                            states[i]=1;
                            states[i+1]=0;
                           
                        }
                        if(states[i+2]==0)
                        {
                            states[i]=0;
                            states[i+2]=1;
                            minStep = recurFind(minStep,oneStep,i,2);
                            states[i]=1;
                            states[i+2]=0;
                           
                        }
                    }
                }
                else if(states[i]==2)
                {
                    if(i<2)
                    {
                        states[i]=0;
                       
                        minStep = recurFind(minStep,oneStep,i,-1-i);
                        states[i]=2;
                    }
                    else
                    {
                        if(states[i-1]==0)
                        {
                            states[i]=0;
                            states[i-1]=2;
                            minStep = recurFind(minStep,oneStep,i,-1);
                            states[i]=2;
                            states[i-1]=0;
                           
                        }
                        if(states[i-2]==0)
                        {
                            states[i]=0;
                            states[i-2]=2;
                            minStep = recurFind(minStep,oneStep,i,-2);
                            states[i]=2;
                            states[i-2]=0;
                           
                        }
                    }
                }
               
            }
            steps[s]=minStep;
            jumps[s]=oneStep;
           
           
        }
        return steps[s];

    }

    private final int recurFind(int minStep, Step oneStep, int pos, int jump) {
        int toS=makeS();
        int r=shortestSteps(toS);
        if(r<minStep-1)
        {
            oneStep.jump=jump;
            oneStep.offset=pos;
            oneStep.jumpTo=toS;
            minStep=r+1;
        }
        return minStep;
    }

   
   
    public void printPath()
    {
        int s=initS;
        int i=1;
       
        while(s!=0)
        {
           
           
            System.out.println("["+(i++)+"] Frog at #"+jumps[s].offset+" jumps #"+jumps[s].jump);
            s=jumps[s].jumpTo;
           
        }
    }
    private final int makeS() {
       
        int r=0;
        int p=1;
        for(int i=0;i<7;i++)
        {
            r+=p*states[i];
            p*=3;
        }
        return r;
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        FrogJump fj=new FrogJump();
        int steps=fj.shortestSteps(fj.initS);
       
        System.out.println("use "+steps+" steps!");
        fj.printPath();

    }

}

运行结果:

use 21 steps!
[
1] Frog at #2 jumps #1
[
2] Frog at #4 jumps #-2
[
3] Frog at #5 jumps #-1
[
4] Frog at #3 jumps #2
[
5] Frog at #1 jumps #2
[
6] Frog at #0 jumps #1
[
7] Frog at #2 jumps #-2
[
8] Frog at #0 jumps #-1
[
9] Frog at #4 jumps #-2
[
10] Frog at #2 jumps #-2
[
11] Frog at #0 jumps #-1
[
12] Frog at #5 jumps #2
[
13] Frog at #3 jumps #2
[
14] Frog at #1 jumps #2
[
15] Frog at #5 jumps #2
[
16] Frog at #3 jumps #2
[
17] Frog at #5 jumps #2
[
18] Frog at #6 jumps #-1
[
19] Frog at #5 jumps #-2
[
20] Frog at #3 jumps #-2
[
21] Frog at #1 jumps #-2








posted @ 2009-01-06 22:27 DoubleH 阅读(1464) | 评论 (3)编辑 收藏

2008年12月10日 #

写一个函数,输出前N个数(从7开始),这N个数满足如下3个条件中的任意一个
1.整出7
2.各位上的数字之和整除7,(比如34)
3.任意位上包含数字7


附我的代码:
void printN(int n)
{

    
    
int c=0;
    
int i=7;
    
do 
    {
        
if(i%7 ==0)
        {
            printf(
"%d\n",i);
            c
++;
        }
        
else
        {
            
int j=i%10;
            
int k=j;
            
int s=k;
            
int p=10;
            
while(k<i)
            {

                
if(j==7)
                {
                    printf(
"%d\n",i);
                    s
=0;
                    c
++;
                    
break;

                }
                
else
                {
                    j
=((i-k)/p)%10;
                    s
+=j;
                    k
=j*p+k;
                    p
*=10;


                }
            }
            
if(s&&s%7==0)
            {


                printf(
"%d\n",i);
                c
++;
            }
            

        }
        i
++;
    } 
while (c<n);
}


posted @ 2008-12-10 21:44 DoubleH 阅读(1703) | 评论 (9)编辑 收藏

2008年6月19日 #

一直以来,整合Apache HTTP Server和其他Java容器时,可能你最好的选择就是mod_jk,但是mod_jk在Apache和外部Java服务器之间使用socket来进行协议转换,性能不能尽如人意。
正如我上一篇博客里说的,mod_java通过在apache嵌入的JVM中直接执行Java来响应HTTP请求,当然是要快于mod_jk的。

但是缺点是,不是Servlet API(虽然实现Servlet API不是很难,但是工作量肯定很大的),优点是,接口很清晰,很灵活,可以做到的事情也就很多。


那么如何开发一个基于mod_java的java handler呢?OK,假设你要在Apache里响应所有/test/*上的请求.
你要做的是:
1)配置Apache启用mod_java
LoadModule java_module modules/mod_java.so

<mod_java your_main_class>
JVMLibrary D:\jdk6\jre\bin\server\jvm.dll
CurDir D:\apache-tomcat-6.0.14
ADDJVMOpt -Djava.class.path=D:\apache-tomcat-6.0.14\bin\bootstrap.jar;D:\dev\vccode\mod_java\mod_java.jar
ADDJVMOpt -Djava.library.path=D:\apache-tomcat-6.0.14\bin
ADDJVMOpt -Dcatalina.home=D:\apache-tomcat-6.0.14
ADDJVMOpt -Duser.dir=D:\apache-tomcat-6.0.14
ADDJVMParam start
ADDJVMStopParam stop
ADDJavaHandler javatest com.yovn.apache.modj.HelloWorld
</mod_java>
这里main_class是可选的,如果有,那么JVM启动时自动调用main方法。

2)在配置文件里加入你将要开发的Java Handler
想上面文件中的
ADDJavaHandler javatest com.yovn.apache.modj.HelloWorld
这里 javatest是handler名字,后面是你的实现的class
3)在配置文件里告诉Apache 你的handler名字对应的路径
<Location /test>
    SetHandler javatest
</Location>

完成这些配置动作后,apache在收到到/test/*的请求后mod_java会call你的handler class的processRequest方法了。

RequestHandler接口如下定义,你的Handler都需要实现该接口:
package com.yovn.apache.modj;

import java.io.IOException;

/**
 * 
@author yovn
 * 
 
*/
public  interface RequestHandler {

    
public abstract void processRequest(ModJRequest request) throws IOException,
            ModJavaException;

}

正如你看到的很简单的接口,但是ModJRequest就不简单了,该接口代表你跟Apache通行的直接的接口,目前定义如下:

package com.yovn.apache.modj;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;

/**
 * 
@author yovn
 *
 
*/
public interface ModJRequest {
    
    
/**
     * 
     * 
@param name
     * 
@return the request header value of that name
     
*/
    String getRequestHeader(String name);
    
    
/**
     * 
     * 
@return similar as HttpServletRequest.getRequestURI()
     
*/
    String getRequestURI();
    
/**
     * 
     * 
@param name header name
     * 
@param value header value
     
*/
    
void setResponseHeader(String name,String value);
    
    
/**
     * 
     * 
@param type 'text/html' etc.. 
     
*/
    
void setContentType(String type);
    
    
/**
     * 
     * 
@param code response code
     
*/
    
void setResponseCode(int code);
    
    
/**
     * 
     * 
@return HTTP method
     
*/
    String getMethod();
    
    
    
/**
     * Give you the  chance when you need push datas to client
     * Also,you can use it to close a connection 
     * Note:
     * HTTP Server may close the connection when it timeout automatically.
     * When you pass use an invalid connection id to call some function , it will failed.
     * 
@see com.yovn.apache.modj.ApacheModule#flushConnection(long, byte[], int, int)
     * 
@see com.yovn.apache.modj.ApacheModule#closeConnection(long)
     * 
@return the connection id for this request.
     
*/
    
long getConnectionId();
    
    
/**
     * same as HttpServletRequest.getServletInputStream
     * 
@return 
     
*/
    InputStream getRequestInputStream();
    
    
/**
     * same as HttpServletResponse.getServletOutputStream
     * 
@return
     
*/
    OutputStream getResponseOutputStream();
    
    
    
/**
     * You should not call the {
@link #getRequestInputStream()} before you call this method this method.
     * In other words, You should either use this method or {
@link #getRequestInputStream()}  to do read data from clients
     * 
@return the direct byte buffer from native side
     * 
@throws IOException
     
*/
    ByteBuffer readTotalRequestBody()
throws IOException;
    
    
    
/**
     * Use apache's apr_send_fd to send a file.
     * Before send file, you may need setup proper HTTP headers, such as 'Content-Disposition' 
     * 
@param fileName
     * 
@return 
     * 
@throws IOException
     
*/
    
boolean sendFile(String fileName)throws IOException;

}
如你所看,基本可以做任何事情(如果还有你想要而没有请一定要告诉我哦)!

下面给个发送文件的例子:

/**
 * 
 
*/
package com.yovn.apache.modj;

import java.io.File;
import java.io.IOException;

/**
 * 
@author yovn
 *
 
*/
public class HelloWorld implements RequestHandler {

    
/**
     * 
     
*/
    
public HelloWorld() {
        
// TODO Auto-generated constructor stub
    }

    
/*
     * (non-Javadoc)
     * 
     * @see com.yovn.apache.modj.RequestHandler#processRequest(ModJRequest request)
     
*/

    
public void processRequest(ModJRequest request) throws IOException,
            ModJavaException {

        request.setContentType(
"APPLICATION/OCTET-STREAM");
    

        File f
=new File("D:\\cspace\\mod_java\\release\\ddd.bin");
        request.setResponseHeader(
"Content-Disposition""attachment;filename=\"ddd.bin\"");
        request.setResponseHeader(
"Content-Length",f.length()+"");
        
        request.sendFile(f.getCanonicalPath());
        

    }

}

下载:
mod_java_0.1

posted @ 2008-06-19 15:38 DoubleH 阅读(2132) | 评论 (5)编辑 收藏

2008年6月18日 #

一 。Apache Java Module是什么?

Apache Java Module是一个Apache2.2 Server下的一个模块,这个模块可以嵌入一个JVM,可以无缝地跟Apache整合在一块,从而便于发布高性能的基于Java的HTTP解决方案。

二。为什么要这么做
1)首先,Apache是HTTP服务器市场的领头羊
2)处于性能的考量。
3)Servlet API有它本身的局限性,例如连接相关的信息基本是被隐藏起来了,这样当你想要异步推数据给客户端时,只能去求助Comet了。
4)只要愿意,我可以同时跑Apache和Tomcat,并在一个进程内同时为两个端口服务。
三。示例

目前初步实现了基本框架,一个Hellow,world的例子见下:
首先配置Apache,在conf文件里加上:
LoadModule java_module modules/mod_java.so

<mod_java org.apache.catalina.startup.Bootstrap>
JVMLibrary D:\jdk1
.6\jre\bin\server\jvm.dll
CurDir D:\apache-tomcat-
6.0.10
ADDJVMOpt -Djava.class.path
=D:\apache-tomcat-6.0.10\bin\bootstrap.jar;D:\cspace\mod_java\mod_java.jar
ADDJVMOpt -Djava.library.path=D:\apache-tomcat-6.0.10\bin
ADDJVMOpt -Dcatalina.home
=D:\apache-tomcat-6.0.10
ADDJVMOpt -Duser.dir
=D:\apache-tomcat-6.0.10
ADDJVMParam start
ADDJVMStopParam stop
ADDJavaHandler javatest com.yovn.apache.modj.HelloWorld
</mod_java>
<Location /javatest>
    SetHandler javatest
</Location>

这段配置脚本,同时会启动一个Tomcat在一个新的线程。
并且,当你请求/javatest/*时,自动会执行com.yovn.apache.modj.HelloWorld来满足这个请求,下面看
这个示例程序:

/**
 * 
 
*/
package com.yovn.apache.modj;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;

/**
 * 
@author yovn
 *
 
*/
public class HelloWorld implements RequestHandler {

    
/**
     * 
     
*/
    
public HelloWorld() {
        
// TODO Auto-generated constructor stub
    }

    
/* (non-Javadoc)
     * @see com.yovn.apache.modj.RequestHandler#processRequest(java.lang.String, int, long, long, java.io.InputStream, java.io.OutputStream)
     
*/
    @Override
    
public void processRequest(String url, int method, long req, long conn,
            InputStream in, OutputStream out) 
throws IOException,
            ModJavaException {
    

        //ApacheModule.setHeader(req, "X-Server", "mod_java");
        out.write("<html><head><title>Hello,World</title></head><body><h1>Hello,World</h1></body></html>".getBytes());
        out.close();

        

    }

}
这是个很简单的程序,当你在浏览器输入http://host:apache_port/javatest/时,显示Hello,World.

目前读取输入数据尚未实现,等完善了我再提供下载文件。


posted @ 2008-06-18 00:05 DoubleH 阅读(1517) | 评论 (1)编辑 收藏

2008年6月1日 #

问题背景
面对艰巨复杂的技术挑战,百度所崇尚的系统设计哲学是“简单可依赖”,而百度的工程师们正在互联网世界中实践着这种理念。这里正好有一个挑战,让作为百度之星的你小试牛刀:

在处理数以百亿计的网络信息的过程中,有一个很常见的问题: 
怎么样将一个集群上的信息以最低的成本传输到另外一个集群上?


数据源集群A有n台服务器,编号为 1, 2, ..., n,i号服务器上待传输的数据量为Ai ,单位是GB。 
目的地集群B有m台服务器,编号为 1, 2, ..., m,j号服务器上的空闲容量为 Bj,单位为 GB。 
A集群的i号服务器上的每GB数据对于B的集群的j号服务器收益为Vi,j,从 A 集群的 i 号服务器向 B 集群的 j 号服务器传输 1GB数据的开销为Ci,j。 
你的任务是在保证A中的所有数据传输完毕的前提下,性价比V/C尽量高。其中V为所有数据在B集群上的价值之和,C为总开销。换句话说,若A集群的i号服务器向B集群的j号服务器发送了Ti,j个GB的数据(Ti,j不一定是整数),则性价比定义为:





输入格式
第1行两个整数n, m(1<=n,m<=50),即集群A和B各自的服务器台数。
第2行包含n个不超过100的正整数A1,A2,…,An,即集群A中每台服务器的待传输数据量(单位:GB)。
第3行包含m个不超过100的正整数B1,B2,…,Bm,即集群B中每台服务器所能接受的最大数据量(单位:GB)。
第 4 ~ n+3 行每行包含m个不超过100的非负整数Vi,j,表示集群A的i号服务器中每GB数据对于集群B中的j号服务器的价值。
第 n+4 ~ 2n+3 行每行包含m个不超过100的正整数Ci,j,表示集群A的i号服务器中每GB数据传输到集群B中的j号服务器所需要的开销。 

输出格式
仅一行,为最大性价比。输出保留三位小数(四舍五入)。如果A的数据无法传输完毕,输出字符串 “-1”(无引号)。 

样例输入
2 2
1 2
2 1
11 0
7 5
6 1
3 2 

样例输出
2.091 

样例解释
一个方案是:
集群A的1号服务器把所有数据传输到集群B的1号服务器,价值11,开销6。
集群A的2号服务器把1GB数据传输到集群B的1号服务器,价值7,开销3,然后把剩下的1GB数据传输到集群B的2号服务器,价值5,开销2。
性价比:(11+7+5)/(6+3+2)=2.091

另一个方案是:
集群A的1号服务器把所有数据传输到集群B的2号服务器,价值0,开销1。
集群A的2号服务器把所有数据传输到集群B的1号服务器,价值14,开销6。
性价比:(0+14)/(1+6)=2。

第一种方案更优

我的解答:
该题应该是贪心法可解,每次求性价比最高的,跟部分背包问题很像。
可惜不是,子问题不是独立的,我的解法肯定不是最优解。sign~~~,据说是最大流的问题,改天研究研究。
我的解法用了N+1个最大值堆,一个是全局所有为传输完的源站点的最高性价比方案,
其余每个源站点一个最大值堆含该站点所有传输方案。

//============================================================================
// Name        : TransportOpt.cpp
// Author      : Yovn
// Version     :
// Copyright   : yovnchine@gmail.com
//============================================================================

#include <iostream>
#include <string>
#include <cstring>
#include <cstdio>

using namespace std;



int numA;
int numB;
int* valuesA;
int* valuesB;
int** values=NULL;
int** costs=NULL;


typedef struct _HeapNode
{
    int a;
    int b;
    float vPerC;
   
}HeapNode;
class MaxHeap
{
public:
    MaxHeap(int n):nodes(new HeapNode[n]),total(n),len(0){
       
    }
    MaxHeap():nodes(NULL),total(0),len(0){
           
    }
    ~MaxHeap()
    {
        delete[] nodes;
    }
    bool isEmpty()
    {
        return len<=0;
    }
    void setSize(int n)
    {
        nodes=new HeapNode[n];
        total=n;
        len=0;
    }
    HeapNode removeMax()
    {
       
        HeapNode ret=nodes[0];
        nodes[0]=nodes[--len];
        shift_down(0);
        return ret;
    }
    void insert(HeapNode val)
    {
       
        nodes[len++]=val;
        shift_up(len-1);
    }
   
private :
   
   
    void shift_up(int pos) {

        HeapNode tmp=nodes[pos];
        int index=(pos-1)/2;
   
        while (index>=0) {
            if (tmp.vPerC>nodes[index].vPerC) {
                nodes[pos]=nodes[index];
                pos=index;
                if (pos==0)
                    break;
                index=(pos-1)/2;
            } else
                break;
        }
        nodes[pos]=tmp;
    }
    void shift_down(int pos) {

        HeapNode tmp=nodes[pos];
        int index=pos*2+1;//use left child
        while (index<len)//until no child
        {
            if (index+1<len&&nodes[index+1].vPerC>nodes[index].vPerC)//right child is smaller
            {
                index+=1;//switch to right child
            }
            if (tmp.vPerC<nodes[index].vPerC) {
                nodes[pos]=nodes[index];
                pos=index;
                index=pos*2+1;

            } else {
                break;
            }

        }
        nodes[pos]=tmp;
       
    }
    HeapNode* nodes;
    int total;
    int len;


   
};

void parseToInts(string& line, int* arr, int num) {
    int pos=0;
    for (int i=0; i<line.length(); i++) {
        if (line[i]>='0'&&line[i]<='9') {
            if (line[i+1]>='0'&&line[i+1]<='9') {
                int a=(line[i]-'0')*10+(line[i+1]-'0');
                arr[pos++]=a;
                i++;
            } else {
                int a=(line[i]-'0');
                arr[pos++]=a;
               
            }
           
        }
    }
}
void input()
{
    string line;
    getline(cin,line);
   
    sscanf(line.c_str(),"%d %d",&numA,&numB);
    valuesA=new int[numA];
    valuesB=new int[numB];
    line.clear();
    getline(cin,line);
    parseToInts(line,valuesA,numA);
   
    line.clear();
    getline(cin,line);
    parseToInts(line,valuesB,numB);
   
  
    values=new int*[numA];
    costs=new int*[numA];
    for (int i=0; i<numA; i++) {
        values[i]=new int[numB];
        line.clear();
        getline(cin, line);
        parseToInts(line, values[i], numB);
    }
   
    for (int i=0; i<numA; i++) {
        costs[i]=new int[numB];
        line.clear();
        getline(cin, line);
        parseToInts(line, costs[i], numB);
    }
   
   
   
}
bool validate() {
    int sumA=0, sumB=0;
    for (int i=0; i<numA; i++) {
        sumA+=valuesA[i];
    }
    for (int i=0; i<numB; i++) {
        sumB+=valuesB[i];
    }
    return sumA<=sumB;
}
void calc() {
    MaxHeap totalHeap(numA);
    MaxHeap* aHeaps=new MaxHeap[numA];
    int totalC=0;
    int totalV=0;

    if(!validate())
    {
        printf("-1\n");
        return;
    }
    for (int i=0; i<numA; i++) {

        aHeaps[i].setSize(numB);
        for(int j=0;j<numB;j++)
        {
            HeapNode node;
            node.a=i;
            node.b=j;
            node.vPerC=(float)values[i][j]/(float)costs[i][j];
            aHeaps[i].insert(node);
        }
        totalHeap.insert(aHeaps[i].removeMax());
    }
    while(!totalHeap.isEmpty())
    {
        HeapNode node=totalHeap.removeMax();
   
        if(valuesA[node.a]==valuesB[node.b])
        {
            totalV+=values[node.a][node.b]*valuesA[node.a];
            totalC+=costs[node.a][node.b]*valuesA[node.a];
            valuesB[node.b]=0;
            valuesA[node.a]=0;
           
        }
        else if(valuesA[node.a]>valuesB[node.b])
        {
            totalV+=values[node.a][node.b]*valuesB[node.b];
            totalC+=costs[node.a][node.b]*valuesB[node.b];
            valuesA[node.a]-=valuesB[node.b];
            valuesB[node.b]=0;
           
        }
        else
        {
            totalV+=values[node.a][node.b]*valuesA[node.a];
            totalC+=costs[node.a][node.b]*valuesA[node.a];
            valuesB[node.b]-=valuesA[node.a];
           
            valuesA[node.a]=0;
       
        }
        while(!aHeaps[node.a].isEmpty())
        {
            HeapNode todo=aHeaps[node.a].removeMax();
            if(valuesA[todo.a]>0&&valuesB[todo.b]>0)
            {
                totalHeap.insert(todo);
                break;
            }
        }
    }
    printf("%lf\n",(float)totalV/totalC);
}
int main() {
   
    input();
    calc();
   
    return 0;
}




posted @ 2008-06-01 18:53 DoubleH 阅读(2109) | 评论 (0)编辑 收藏

2008年5月31日 #

问题背景

有一种简单的网页判重的方法,通过求两个网页内容的最长公共子序列(LCS)长度来判定两个网页的相似程度。如:
(网页A)老师:请用“果然”造句。
(网页B)学生:先吃水果,然后喝汽水……
它们的最长公共子序列为“果然”,长度为2。注意这里的“子序列”并不要求连续。

类似的,下面两个网页:
(网页A)老师:请用“果然”造句。
(网页B)学生:先吃水果,然后喝汽水,果然拉肚子……

最长公共子序列还是“果然”,长度为2。但不难看出,由于“果然”两个字在网页B中也曾连续出现,第二组网页比第一组更加“相似”。为了区分开这两种情况的区分度,我们改用一种称为LZW的理论。为了严格的叙述相似度的计算方法,我们首先定义“文本单元”。

假定网页用一个不包含空白字符(空格、回车换行、水平制表符)的字符串来表示。它只包含纯文本,没有标签。在计算相似度之前,你应该首先对该字符串进行处理,划分成一个个“文本单元”。每个文本单位可以是一个中文字、英文单词(由一个或多个连续的半角英文字母和数字组成,正规表达式为[a-zA-Z0- 9]+)、或者一个标点符号。

根据上述定义,同一个标点符号的全角和半角应该被作为不同的文本单元,尽管他们看起来可能很相近;每个单独全角英文和全角数字都应该被看成一个单独的文本单元,而连续的半角英文字母和数字应被看成一个整体。总之,全角的字符可以与中文字同等对待。

这样,网页被看成文本单元序列。例如,网页“内容?123456??web2.00#”切分出的文本单元序列为(为了显示方便,用下划线分隔各文本单元):
内_容_?_1_2_345_6_?_?_web2_._00_#

而网页“why内容相似??1234567890,web#00”的切分结果为:
why_内_容_相_似_?_?_1234567890_,_web_#_00

黑体部分给出了两个网页的一个公共子序列。注意“内容”、“??”分别在两个网页中都是连续出现的文本单元。为了奖励这种情况,LZW规定一段由连续k个文本单元组成的字符串权值为k^2。在刚才的例子中,“内容”、“??”的权值均为4。但“00”是一个数字串,应当被看成一个单独的文本单元。所以权值仅为1。

根据上述规则,公共子序列“内容 ?? 00”的权值为2^2+2^2+1=9。在所有可能的子序列中,这个权值是最大的。

给定两个网页,求他们的LZW相似度,即所有可能的公共子序列中的最大权值。

注意

1) 输入的网页内容以GBK编码(参见FAQ)
2) 除了大小写英文字母和数字之外的其他半角字符均视为标点符号。
输入格式

包含两行,分别是网页A和B对应的字符串(不包含空白字符)。每行至少包含5个字节,最多包含200个字节。
输出格式

输出仅一行,包含一个整数,为两个网页的LZW相似度。
样例输入

内容?123456??web2.00#
why内容相似??1234567890,web#00
样例输出
9


解答:
该题主要分两部分,一部分就是解析成文本单元,另一部分就是LZW的实现,LZW其实是最长公共子序列算法的一个变形。

//============================================================================
// Name        : LZW.cpp
// Author      : Yovn
// Version     :
// Copyright   : yovnchine@gmail.com
//============================================================================

#include 
<iostream>
#include 
<string>
#include 
<cstring>
using namespace std;

void parseToLZWLine(const char* in, int inLen, char**& out, int& actualLen) {
    
int mark=0;
    actualLen
=0;
    out
=new char*[inLen];

    
for (int i=0; i<inLen; i++) {
        
char ch=in[i];
        
if (ch<0) {
            
if (mark<i) {
                out[actualLen]
=new char[i-mark+1];
                strncpy(out[actualLen], in
+mark, i-mark);
                out[actualLen][i
-mark]='\0';
                actualLen
++;
            }
            out[actualLen]
=new char[3];
            out[actualLen][
0]=ch;
            out[actualLen][
1]=in[++i];
            out[actualLen][
2]='\0';
            actualLen
++;

            mark
=i+1;
        } 
else if ((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z')||(ch>='0'&&ch<='9')) {
            
continue;
        } 
else//only one case left
        {
            
if (mark<i) {
                out[actualLen]
=new char[i-mark+1];
                strncpy(out[actualLen], in
+mark, i-mark);
                out[actualLen][i
-mark]='\0';
                actualLen
++;

            }
            out[actualLen]
=new char[2];
            out[actualLen][
0]=ch;
            out[actualLen][
1]='\0';
            actualLen
++;
            mark
=i+1;
        }
    }
    
if (mark<inLen) {
        out[actualLen]
=new char[inLen-mark+1];
        strncpy(out[actualLen], in
+mark, inLen-mark);
        out[actualLen][inLen
-mark]='\0';
        actualLen
++;

    }
}
void printLZWStr(char** lzwStr, int lzwLen) {
    
for (int i=0; i<lzwLen; i++) {
        printf(
"%s", lzwStr[i]);
        
if (i<lzwLen-1) {
            printf(
"_");
        }
    }
    printf(
"\n");
}
void freeLZWStr(char** lzwStr, int lzwLen) {
    
for (int i=0; i<lzwLen; i++) {
        delete[] lzwStr[i];
    }
    delete[] lzwStr;
}

int calcLZW(char** left, int leftLen, char** right, int rightLen) {
    
//printLZWStr(left, leftLen);
    
//printLZWStr(right, rightLen);
    int** result=new int*[leftLen+1];
    
int** len=new int*[leftLen+1];
    
for (int i=0; i<=leftLen; i++) {
        result[i]
=new int[rightLen+1];
        len[i]
=new int[rightLen+1];
        result[i][
0]=0;
        len[i][
0]=0;
    }
    memset(result[
0], 0, sizeof(int)*(rightLen+1));
    memset(len[
0], 0, sizeof(int)*(rightLen+1));
    
for (int i=1; i<=leftLen; i++) {
        
for (int j=1; j<=rightLen; j++) {
            
if (strcmp(left[i-1], right[j-1])==0) {
                
//back trace one

                len[i][j]
=len[i-1][j-1]+1;
                result[i][j]
=result[i-1][j-1]-(len[i-1][j-1]*len[i-1][j-1])
                        
+(len[i][j]*len[i][j]);

            } 
else if (result[i-1][j]>=result[i][j-1]) {
                result[i][j]
=result[i-1][j];
            } 
else {
                result[i][j]
=result[i][j-1];
            }

        }
    }

    
int ret= result[leftLen][rightLen];
    
for (int i=0; i<=leftLen; i++) {
        delete[] result[i];
        delete[] len[i];

    }
    delete[] result;
    delete[] len;
    
return ret;

}

int main() {
    
char** lzwStr1=NULL;
    
char** lzwStr2=NULL;
    string str1;
    string str2;
    
int lzwLen1=0;
    
int lzwLen2=0;
    getline(cin,str1);
    getline(cin,str2);
    
    parseToLZWLine(str1.c_str(), strlen(str1.c_str()), lzwStr1, lzwLen1);
    parseToLZWLine(str2.c_str(), strlen(str2.c_str()), lzwStr2, lzwLen2);

    cout
<<calcLZW(lzwStr1, lzwLen1, lzwStr2, lzwLen2)<<endl;

    freeLZWStr(lzwStr1, lzwLen1);
    freeLZWStr(lzwStr2, lzwLen2);

    
return 0;
}



posted @ 2008-05-31 23:20 DoubleH 阅读(1952) | 评论 (3)编辑 收藏

仅列出标题  下一页