Categories
不学无术

KM算法 详解+模板

本文转载自:http://blog.sina.com.cn/s/blog_691ce2b701016reh.html
 
先说KM算法求二分图的最佳匹配思想,再详讲KM的实现。
【KM算法求二分图的最佳匹配思想】

对于具有二部划分( V1, V2 )的加权完全二分图,其中 V1= { x1, x2, x3, … , xn }, V2= { y1, y2, y3, … , yn },边< xi, yj >具有权值 Wi,j 。该带权二分图中一个总权值最大的完美匹配,称之为最佳匹配。
 
记 L(x) 表示结点 x 的标记量,如果对于二部图中的任何边<x,y>,都有 L(x)+ L(y)>= Wx,y,我们称 L 为二部图的可行顶标。
设 G(V,E) 为二部图, G'(V,E’) 为二部图的子图。如果对于 G’ 中的任何边<x,y> 满足, L(x)+ L(y)== Wx,y,我们称 G'(V,E’) 为 G(V,E) 的等价子图。
 
定理一:设 L 是二部图 G 的可行顶标。若 L 等价子图 GL 有完美匹配 M,则 M 是 G 的最佳匹配。
证明:由于 GL 是 G 的等价子图,M 是 GL 的完美匹配,所以,M 也是 G  的完美匹配。以由于对于匹配 M 的每条边 e ,都有 e∈ E( GL ),而且 M 中每条边覆盖每个顶点正好一次,所以
W( M )= å W(e), e∈ M = å L(x), x∈ V
另一方面,对于 G 的任何完美匹配 M’ 有
W( M’ )= å W(e), e∈ M’ <= å L(x), x∈ V
于是 W( M )>= W( M’ ),即 M 是 G 的最优匹配。
 
由上述定理,我们可以通过来不断修改可行顶标,得到等价子图,从而求出最佳匹配。
就像匈牙利算法一样,我们依次为每一个顶点 i 寻找增广路径,如果寻找增广路径失败,我们就修改相应的可行顶标,来得到增广路径。
如图:
|  1  2  3  |
|  3  2  4  |
|  2  3  5  |
若要对这个完全二分图求最佳匹配
 
初始化:
Lx(1)= max{ y| w(1,y), 1<= y<= 3 }= max{ 1, 2, 3 }= 3, Ly(1)= 0
Lx(2)= max{ 3, 2, 4 }= 4, Ly(2)= 0
Lx(3)= max{ 2, 3, 5 }= 5, Ly(3)= 0;
我们建立等价子图( 满足 Lx(x)+ Ly(y)== W(x,y) ) 如下:
km算法求二分图最佳匹配
对于该图,运用匈牙利算法对 X 部顶点 1 求增广路径,得到一个匹配,如图( 红色代表匹配边 ):km算法求二分图最佳匹配
 对 X 部顶点 2 求增广路径失败,寻找增广路径的过程为 X 2-> Y 3-> X 1。我们把寻找增广路径失败的 DFS 的交错树中,在 X 部顶点集称之为 S, 在 Y 部的顶点集称之为 T。则 S= { 1, 2 },T= { 3 }。现在我们就通过修改顶标值来扩大等价子图,如何修改。
 
1)   我们寻找一个 d 值,使得 d= min{ (x,y)| Lx(x)+ Ly(y)- W(x,y), x∈ S, y∉ T },因些,这时 d= min{
Lx(1)+Ly(1)-W(1,1),  Lx(1)+Ly(2)-W(1,2),  Lx(2)+Ly(1)-W(2,1),  Lx(2)+Ly(2)-W(2,2) }=
min{ 3+0- 1, 3+0-2,  4+0-3,  4+0-2 }= min{ 2, 1, 1, 2 }= 1。
寻找最小的 d 是为了保证修改后仍满足性质对于边 <x,y> 有 Lx(x)+ Ly(y)>= W(x,y)。
 
2)   然后对于顶点 x
1. 如果 x∈ S 则 Lx(x)= Lx(x)- d。
2. 如果 x∈ T 则 Ly(x)= Ly(x)+ d。
3. 其它情况保持不变。
如此修改后,我们发现对于边<x,y>,顶标 Lx(x)+ Ly(y) 的值为
1.  Lx(x)- d+ Ly(y)+ d,  x∈ S, y∈ T。
2.  Lx(x)+ Ly(y),  x∉ S,  y∉ T。
3.  Lx(x)- d+ Ly(y), x∈ S, y∉ T。
4.  Lx(x)+ Ly(y)+ d, x∉ S,  y∈ T。
易知,修改后对于任何边仍满足 Lx(x)+ Ly(y)>= W(x,y),并且第三种情况顶标值减少了 d,如此定会使等价子图扩大。
 
就上例而言: 修改后 Lx(1)= 2, Lx(2)= 3, Lx(3)= 5, Ly(1)= 0, Ly(1)= 0, Ly(2)= 0, Ly(3)= 1。
这时 Lx(2)+Ly(1)=3+0=3= W(2,1),在等价子图中增加了一条边,等价子图变为:
 km算法求二分图最佳匹配
如此按以上方法,得到等价子图的完美匹配。
 
另外计算 d 值的时候可以进行一些优化。
定义 slack(y)= min{ (x,y)| Lx(x)+ Ly(y)- W(x,y),x∈ S,  y∉ T }
这样能在寻找增广路径的时候就顺便将 slack 求出。

(以上为摘上网络)
【KM算法及其具体过程】
(1)可行点标:每个点有一个标号,记lx[i]为X方点i的标号,ly[j]为Y方点j的标号。如果对于图中的任意边(i, j, W)都有lx[i]+ly[j]>=W,则这一组点标是可行的。特别地,对于lx[i]+ly[j]=W的边(i, j, W),称为可行边;
(2)KM 算法的核心思想就是通过修改某些点的标号(但要满足点标始终是可行的),不断增加图中的可行边总数,直到图中存在仅由可行边组成的完全匹配为止,此时这个 匹配一定是最佳的(因为由可行点标的的定义,图中的任意一个完全匹配,其边权总和均不大于所有点的标号之和,而仅由可行边组成的完全匹配的边权总和等于所 有点的标号之和,故这个匹配是最佳的)。一开始,求出每个点的初始标号:lx[i]=max{e.W|e.x=i}(即每个X方点的初始标号为与这个X方 点相关联的权值最大的边的权值),ly[j]=0(即每个Y方点的初始标号为0)。这个初始点标显然是可行的,并且,与任意一个X方点关联的边中至少有一条可行边;
(3)然后,从每个X方点开始DFS增广。DFS增广的过程与最大匹配的Hungary算法基本相同,只是要注意两点:一是只找可行边,二是要把搜索过程中遍历到的X方点全部记下来(可以用vst搞一下),以进行后面的修改;
(4) 增广的结果有两种:若成功(找到了增广轨),则该点增广完成,进入下一个点的增广。若失败(没有找到增广轨),则需要改变一些点的标号,使得图中可行边的 数量增加。方法为:将所有在增广轨中(就是在增广过程中遍历到)的X方点的标号全部减去一个常数d,所有在增广轨中的Y方点的标号全部加上一个常数d,则 对于图中的任意一条边(i, j, W)(i为X方点,j为Y方点):
<1>i和j都在增广轨中:此时边(i, j)的(lx[i]+ly[j])值不变,也就是这条边的可行性不变(原来是可行边则现在仍是,原来不是则现在仍不是);
<2>i在增广轨中而j不在:此时边(i, j)的(lx[i]+ly[j])的值减少了d,也就是原来这条边不是可行边(否则j就会被遍历到了),而现在可能是;
<3>j在增广轨中而i不在:此时边(i, j)的(lx[i]+ly[j])的值增加了d,也就是原来这条边不是可行边(若这条边是可行边,则在遍历到j时会紧接着执行DFS(i),此时i就会被遍历到),现在仍不是;
<4>i和j都不在增广轨中:此时边(i, j)的(lx[i]+ly[j])值不变,也就是这条边的可行性不变。
这 样,在进行了这一步修改操作后,图中原来的可行边仍可行,而原来不可行的边现在则可能变为可行边。那么d的值应取多少?显然,整个点标不能失去可行性,也 就是对于上述的第<2>类边,其lx[i]+ly[j]>=W这一性质不能被改变,故取所有第<2>类边的 (lx[i]+ly[j]-W)的最小值作为d值即可。这样一方面可以保证点标的可行性,另一方面,经过这一步后,图中至少会增加一条可行边。
(5)修改后,继续对这个X方点DFS增广,若还失败则继续修改,直到成功为止;
(6)以上就是KM算法的基本思路。但是朴素的实现方法,时间复杂度为O(n4)——需要找O(n)次增广路,每次增广最多需要修改O(n)次顶标,每次修改顶 标时由于要枚举边来求d值,复杂度为O(n2)。实际上KM算法的复杂度是可以做到O(n3)的。我们给每个Y顶点一个“松弛量”函数slack,每次开 始找增广路时初始化为无穷大。在寻找增广路的过程中,检查边(i,j)时,如果它不在相等子图中,则让slack[j]变成原值与 A[i]+B[j]-w[i,j]的较小值。这样,在修改顶标时,取所有不在交错树中的Y顶点的slack值中的最小值作为d值即可。但还要注意一点:修 改顶标后,要把所有不在交错树中的Y顶点的slack值都减去d。
【求二分图的最小匹配】
只需把权值取反,变为负的,再用KM算出最大权匹配,取反则为其最小权匹配。

hdoj 2255
#include 
#include 
#define M 310
#define inf 0x3f3f3f3f
int n,nx,ny;
int link[M],lx[M],ly[M],slack[M];    //lx,ly为顶标,nx,ny分别为x点集y点集的个数
int visx[M],visy[M],w[M][M];
int DFS(int x)
{
    visx[x] = 1;
    for (int y = 1;y <= ny;y ++)
    {
        if (visy[y])
            continue;
        int t = lx[x] + ly[y] - w[x][y];
        if (t == 0)       //
        {
            visy[y] = 1;
            if (link[y] == -1||DFS(link[y]))
            {
                link[y] = x;
                return 1;
            }
        }
        else if (slack[y] > t)  //不在相等子图中slack 取最小的
            slack[y] = t;
    }
    return 0;
}
int KM()
{
    int i,j;
    memset (link,-1,sizeof(link));
    memset (ly,0,sizeof(ly));
    for (i = 1;i <= nx;i ++)            //lx初始化为与它关联边中最大的
        for (j = 1,lx[i] = -inf;j <= ny;j ++)
            if (w[i][j] > lx[i])
                lx[i] = w[i][j];
    for (int x = 1;x <= nx;x ++)
    {
        for (i = 1;i <= ny;i ++)
            slack[i] = inf;
        while (1)
        {
            memset (visx,0,sizeof(visx));
            memset (visy,0,sizeof(visy));
            if (DFS(x))     //若成功(找到了增广轨),则该点增广完成,进入下一个点的增广
                break;  //若失败(没有找到增广轨),则需要改变一些点的标号,使得图中可行边的数量增加。
                        //方法为:将所有在增广轨中(就是在增广过程中遍历到)的X方点的标号全部减去一个常数d,
                        //所有在增广轨中的Y方点的标号全部加上一个常数d
            int d = inf;
            for (i = 1;i <= ny;i ++)
                if (!visy[i]&&d > slack[i])
                    d = slack[i];
            for (i = 1;i <= nx;i ++)
                if (visx[i])
                    lx[i] -= d;
            for (i = 1;i <= ny;i ++)  //修改顶标后,要把所有不在交错树中的Y顶点的slack值都减去d
                if (visy[i])
                    ly[i] += d;
                else
                    slack[i] -= d;
        }
    }
    int res = 0;
    for (i = 1;i <= ny;i ++)
        if (link[i] > -1)
            res += w[link[i]][i];
    return res;
}
int main ()
{
    int i,j;
    while (scanf ("%d",&n)!=EOF)
    {
        nx = ny = n;
      //  memset (w,0,sizeof(w));
        for (i = 1;i <= n;i ++)
            for (j = 1;j <= n;j ++)
                scanf ("%d",&w[i][j]);
        int ans = KM();
        printf ("%dn",ans);
    }
    return 0;
}
Categories
不学无术 木有技术

协同过滤文章两篇

探索推荐引擎内部的秘密,第 2 部分_ 深入推荐引擎相关算法 – 协同过滤 协同过滤

Categories
不学无术

interval scale和ratio scale的区别

interval scale 等距尺度:又称区间尺度,衡量的数字可代表大小或优劣顺序,数字的差距直接表示其差异的程度。
ratio scale 比例尺度:物与物的相对比较,表明各种物体间的相对度量关系。
参考百度百科

Categories
不学无术

Eclipse JRI : Java 调用 R 报错: Cannot find JRI native library!

Q: I get the following error, what’s wrong?
java.lang.UnsatisfiedLinkError: no jri in java.library.path
A: Usually it means that you didn’t setup the necessary environment variables properly or the JRI library is not where it is expected to be. The recommended way to start JRI programs is to use the run script which is generated along with the library. It sets everything up and is tested to work. If you want to write your own script or launcher, you must observe at least the following points:

  • R_HOME must be set correctly
  • (Windows): The directory containing R.dll must be in your PATH
  • (Mac): Well, it’s a Mac, so it just works ;).
  • (unix): R must be compiled using --enable-R-shlib and the directory containing libR.so must be in LD_LIBRARY_PATH. Also libjvm.so and other dependent Java libraries must be on LD_LIBRARY_PATH.
  • JRI library must be in the current directory or any directory listed in java.library.path. Alternatively you can specify its path with 
    -Djava.library.path= when starting the JVM. When you use the latter, make sure you check java.library.path property first such that you won’t break your Java.
  • Depending on your system, the R verison and other features you want to use, you may have to set additional settings such as R_SHARE_DIR, R_INCLUDE_DIR andR_DOC_DIR.

Again, I don’t think you want to worry about all of the above – just use the start script!

特别注意红字标注的这一行,
QQ截图20130728131711
在这边修改实现即可。
参考:http://www.rforge.net/JRI/

Categories
不学无术

JXL Excel使用

http://www.360doc.com/content/11/0422/14/6728052_111518047.shtml

Categories
不学无术

1028. List Sorting (25) | STL中自定义排序

时间限制

200 ms
内存限制
32000 kB
代码长度限制
16000 B
Excel can sort records according to any column. Now you are supposed to imitate this function.
Input
Each input file contains one test case. For each case, the first line contains two integers N (<=100000) and C, where N is the number of records and C is the column that you are supposed to sort the records with. Then N lines follow, each contains a record of a student. A student’s record consists of his or her distinct ID (a 6-digit number), name (a string with no more than 8 characters without space), and grade (an integer between 0 and 100, inclusive).
Output
For each test case, output the sorting result in N lines. That is, if C = 1 then the records must be sorted in increasing order according to ID’s; if C = 2 then the records must be sorted in non-decreasing order according to names; and if C = 3 then the records must be sorted in non-decreasing order according to grades. If there are several students who have the same name or grade, they must be sorted according to their ID’s in increasing order.
Sample Input 1

3 1
000007 James 85
000010 Amy 90
000001 Zoe 60

Sample Output 1

000001 Zoe 60
000007 James 85
000010 Amy 90

Sample Input 2

4 2
000007 James 85
000010 Amy 90
000001 Zoe 60
000002 James 98

Sample Output 2

000010 Amy 90
000002 James 98
000007 James 85
000001 Zoe 60

Sample Input 3

4 3
000007 James 85
000010 Amy 90
000001 Zoe 60
000002 James 90

Sample Output 3

000001 Zoe 60
000007 James 85
000002 James 90
000010 Amy 90

============================================
这个题目能想到最好的办法,就是用set然后重载一个自定义排序了 感谢郭嘉,感谢STL!
set这个东西嘛,好像是用红黑树还是什么实现的(印象中是这样的) 原来打算用vector的,但是vector好像不能在插入的时候就根据自定义的排序方法排序好插进去,这样如果全部录入完了整体排序一遍是不是更耗费时间呢?
参考文章:
http://blog.csdn.net/stone_sky/article/details/8471722 讲的是自定义排序 http://zhidao.baidu.com/question/189798009.html 讲的是输出填0格式化问题 http://www.189works.com/article-43335-1.html http://wenku.baidu.com/view/08c0eb0bff00bed5b9f31d29.html 这两个就是自定义排序了
这题目这么搞了以后就简单多了:)当然最后一个测试点还是用了100ms…不管了过了就行 ============================================

#include
#include
#include
#include
using namespace std;
int compareName(const char name1[],const  char name2[])
{
    for(int i=0; i<8; i++)
    {
        if( name1[i] < name2[i] )
            return 1;
        else if(name1[i] > name2[i])
            return -1;
        //相等的话继续比较
    }
    return 0; //相同的名字
}
struct StudentRecord
{
    int ID;
    char name[9];
    int grade;
    int sortType; //按哪一列排序,1,2,3
    StudentRecord(){}
    StudentRecord(int _id, char _name[], int _grade, int _sortType)
    {
        ID = _id;
        strcpy(name, _name);
        grade = _grade;
        sortType = _sortType;
    }
    void set(int _id, char _name[], int _grade)
    {
        ID = _id;
        strcpy(name, _name);
        grade = _grade;
    }
    //自定义排序方法
    bool operator< (const StudentRecord& other) const
    {
        int cmpResult;
        switch(sortType)
        {
        case 1:
            //按照ID升序排列
            return ID < other.ID;
        case 2:
            //按照姓名的升序排列,如果有同名再按ID升序
            cmpResult = compareName(name, other.name);
            if(cmpResult > 0)
                return true;
            else if (cmpResult < 0)
                return false;
            else
                return ID < other.ID;
        case 3:
            //按照成绩的升序排列,如果有同名成绩按ID升序
            if (grade == other.grade)
                return ID < other.ID;
            else
                return grade < other.grade;
        default:
            printf("ERROR!");
            return false;
        }
    }
};
set records;
int main()
{
    long N; int C;
    scanf("%ld %d", &N, &C);
    StudentRecord singleRec;
    singleRec.sortType = C;
    for(long i=0; i<N; i++)
    {
        int ID, grade;
        char name[8];
        scanf("%d %s %d", &ID, name, &grade);
        singleRec.set(ID, name, grade);
        records.insert(singleRec);
    }
    set::iterator it;
    for(it = records.begin(); it != records.end(); it++)
    {
        StudentRecord sr = *it;
        printf("%06d %s %dn", sr.ID, sr.name, sr.grade);
    }
    return 0;
}

====================================

测试点

测试点 结果 用时(ms) 内存(kB) 得分/满分
0 答案正确 0 790 5/5
1 答案正确 0 740 5/5
2 答案正确 0 750 5/5
3 答案正确 0 790 2/2
4 答案正确 0 790 2/2
5 答案正确 0 740 2/2
6 答案正确 100 8970 4/4
Categories
不学无术

c++中vector自定义排序的问题

http://blog.csdn.net/stone_sky/article/details/8471722

Categories
木有技术

Visual Studio 2012 改Tab制表符为4个空格

之前没发现这个问题,后来看到自己发的博客里面代码各种参差不齐…想到有可能是这个东西的关系…
其实对于C++来说Tab键还不是致命的,要是写Python的话Tab和空格弄不清就爽翻了..~当然这个是题外话了,没人用VS写Python的吧:)
步骤如图,不赘述了~
QQ截图20130725132639
QQ截图20130725132700 QQ截图20130725132706
 
然后确定
 
其他软件的可以参考此文:
http://blog.csdn.net/wfdtxz/article/details/8520629

Categories
不学无术

1009. Product of Polynomials (25)

时间限制
400 ms
内存限制
32000 kB
代码长度限制
16000 B
This time, you are supposed to find A*B where A and B are two polynomials.
Input Specification:
Each input file contains one test case. Each case occupies 2 lines, and each line contains the information of a polynomial: K N1 aN1 N2 aN2 … NK aNK, where K is the number of nonzero terms in the polynomial, Ni and aNi (i=1, 2, …, K) are the exponents and coefficients, respectively. It is given that 1 <= K <= 10, 0 <= NK < … < N2 < N1 <=1000.
 
Output Specification:
For each test case you should output the product of A and B in one line, with the same format as the input. Notice that there must be NO extra space at the end of each line. Please be accurate up to 1 decimal place.
Sample Input

2 1 2.4 0 3.2
2 2 1.5 1 0.5

Sample Output

3 3 3.6 2 6.0 1 1.6

=====================================
这题目没什么难度的说,就是多项式乘积嘛~
唯一要注意的事情是,结果的输出时按照指数递减的…这里用了map里面的reverse_iterator,真心是好用!
另外,也不知道系数有没有负数,所以处理的时候考虑了一下相加为0的情况…貌似没啥必要哦…
总之是很简单的东西~前提是有STL这货…
=====================================

#include 
#include 
using namespace std;
#define MapIterator map::iterator
#define MapIteratorReverse map::reverse_iterator
int main()
{
    int K;
    scanf("%d", &K);
    map polyA; //第一行
    for(int i=0; i p(Ni, Ki);
        polyA.insert(p);
    }
    scanf("%d", &K);
    map result; //结果
    for(int i=0; ifirst + Ni;  //乘方
            float Kj = it->second * Ki;//系数
            if(result.count(Nj) > 0)
            {
                //原来有结果
                float sum = Kj + result[Nj];
                if(sum == 0)
                    //这项没了...
                    result.erase(Nj);
                else
                    result[Nj] = sum;
            }
            else
            {
                result.insert(pair(Nj, Kj));
            }
        }
    }
    printf("%d", result.size());
    MapIteratorReverse it;
    for(it = result.rbegin(); it != result.rend(); it++)
    {
        printf(" %d %.1f", it->first, it->second);
    }
    return 0;
}
Categories
不学无术 木有技术

Eclipse中文字体太小的解决方法

原文来自:http://www.cnblogs.com/newdon318/archive/2012/03/23/2413340.html
最近新装了Win7,打开eclipse3.7中文字体很小,简直难以辨认。在网上搜索发现这是由于Eclipse 3.7 用的字体是 Consolas,显示中文的时候默认太小了。
解决方式有两种:
一、把字体设置为Courier New
操作步骤:打开Elcipse,点击菜单栏上的“Windows”——点击“Preferences”——点击“Genneral”——点击“Appearance”——点击“Colors and Font”——在右侧框展开“Basic”文件夹–双击“Text Font”——在弹出窗选择“Courier New”(注:这里可能找不到“Courier New”,点击字体选择框左下角的“显示更多字体”链接来打开设置字体的控制面板,找到“Courier New”,右键选择“显示”即可激活该字体)——点击按钮“确定”——点击按钮“OK”,完成。
二、使用混合字体代替Consolas字体。
操作步骤:
1.下载Consolas和微软雅黑混合字体(地址:http://files.cnblogs.com/icelyb24/YaHei.Consolas.1.12.rar)
2.解压之后,把YaHei.Consolas.1.12.ttfw文件复制到C:WindowsFonts目录下,完成字体的安装
3.打开Elcipse,点击菜单栏上的“Windows”——点击“Preferences”——点击“Genneral”——点击“Appearance”——点击“Colors and Font”——在右侧框展开“Basic”文件夹–双击“Text Font”——在弹出窗选择“YaHei.Consolas”——点击按钮“确定”——点击按钮“OK”,完成。