C++ notes

群里看到一个很有趣的东西

int a=(int)((int*)0+4);

乍一看似乎狂拽酷炫,后来发现int和int 之间转化也是地址值的直接转化,(int)0先变成地址值为0的指针(或者称职NULL,VS认为是等价的),然后指针+4相当于+4*sizeof(int), 相当于地址值+16,然后将0x00000010(VS似乎是32bit的,32bit,然后直接将地址值转化为int就是值),a=16

看到微博粉丝狂魔陈立人发了一个面试题,求K linklist的交集,返回一个linklist,类似于Merge K linklist,当时需要用heap优化才能AC。

一开始写个代码,大概想了下可以heap,但是后来发现似乎不行,因为merge是把数据删掉,同时copy到result linklist里,但是这里如果不是全部相等,需要将非max的val对应的linklist指针
后移,同时max的还要保留,heap也必须是maxheap,似乎是不行,于是只好写个朴素的O(n)的遍历了。不确定是否bug free

struct ListNode
{
    int val;
    ListNode* next;
    ListNode(int x):val(x), next(NULL)
    {
        //val=x;
        //next=NULL;
    }
};

bool AtLeastOnePointNULL()
{
    for(int i=0;i<p.size();i++)
        if(p[i]==NULL) 
            return true;
    return false;
}

ListNode* Intersection(vector<ListNode*> ListHead)
{
    vector<ListNode*> p;
    //vector<int> nextp;
    ListNode* resulthead=NULL, *resulttail;

    if(ListHead.size()==0)
        return resulthead;
    //vector<int> curval;
    for(int i=0;i<ListHead.size();i++)
        p.push_back(ListHead[i]);//curval.push_back(ListHead[i]->val);
    //make_heap(curval.begin(),curval.end());//max heap default
    bool first=true;
    while(1)
    {
        if(AtLeastOnePointNULL())
            break;
        int max=p[0]->val;//at least one linklist
        bool modified=false;
        for(int i=1;i<p.size();i++)
            if(p[i]->val>max)
                max=p[i]->val,modified=true;
        if(modified==false)//at least one linklist        
        {
            if(first==true)
            {
                resulthead=new ListNode(p[0]->val);
                resulttail=resulthead;
                first==false;
            }
            else
            {
                resulttail->next=new ListNode(p[0]->val),resulttail=resulttail->next;
            }
            for(int i=0;i<p.size();i++)
                p[i]=p[i]->next;
        }
        else
        {
            for(int i=0;i<p.size();i++)
            {
                if(p[i]->val<max)
                    p[i]=p[i]->next;
            }
        }
    }

    return resulthead;
}

昨晚和瞿神讨论这道算法,发现似乎我的稍微有点笨了点,可以一次遍历第二遍遍历的次数,这个时间复杂度不能从循环的角度来计算,因为条件判断比较复杂。
这道应该从结点的某个操作次数累加来看,例如K个Linklist共N个结点,然后每个结点一定只是后移一次,这里面主要就是point后移操作,只要一个linklist空了就结束,
时间复杂度是O(N)

ListNode* Intersection(vector<ListNode*> ListHead)
{
    vector<ListNode*> p;
    vector<int> curmax;

    ListNode* head=NULL,*tail;

    if(ListHead.size()==0)
        return head;
    for(int i=0;i<ListHead.size();i++)
        p.push_back(ListHead[i]);
    bool first=false;
    while(1)
    {
        if(AtLeastOnePointNULL())
            break;
        curmax.push_back(0);
        for(int i=1;i<p.size();i++)
        {
            if(p[i]->val<p[curmax[0]]->val)//at least one ele in curmax
                p[i]=p[i]->next;
            else if(p[i]->val==p[curmax[0]]->val)
                curmax.push_back(i);
            else//update max
            {
                for(int j=0;j<curmax.size();j++)
                    p[curmax[j]]=p[curmax[j]]->next;
                curmax.clear();
                curmax.push_back(i);
            }
        }
        if(curmax.size()==ListHead.size())
        {
            if(first==false)
            {
                head=new ListNode(p[0]->val);
                tail=head;
                first==true;
            }
            else
            {
                tail->next=new ListNode(p[0]->val),tail=tail->next;
            }
            for(int i=0;i<p.size();i++)
            {
                p[i]=p[i]->next;
            }
        }
    }
    return head;
}

今天碰到一个subfigure的问题,用BMC Bio的模板怎么改都改不好,主要是width height加不进去,识别不了,因此两个subfigure是上下排列的。
周老师过来问我屏幕怎么两个,如果读了博,肯定是不会问了,还好我是拿旧的屏幕,如果新的估计肯定被换掉了。

今天看到陈立人公布了答案,还是用堆来做的,但是不是之前想的最大堆,而是最小堆,同时维护一个最大值,这样保证每次都知道当前堆中的最大值。
算法中维护这个词是很微妙的,我不是很习惯用,导致经常想到遍历一遍当前找最大,其实完全没必要。比较当前堆顶和当前最大值大小,便可以知道是否是交集了,
如果最小的等于最大值,那整个堆都是同一元素,否则需要逐个出堆直到遇到堆顶等于最大值(不可能大于)。但是似乎不需要存储

struct ListNode
{
    int val;
    ListNode* next;
    ListNode(int x): val(x), next(NULL)
    {
    }
};

bool comp(ListNode* p1,ListNode*p2)
{
    return p1->val>p2->val;
}
bool AtLeastOnePointNULL()
{
    for(int i=0;i<(int)p.size();i++)
        if(p[i]==NULL)
            return true;
    return false;
}

ListNode* Intersection(vector<ListNode* > ListHead)
{
    int max=0x80000000;
    //0x7FFFFFFF;
    vector<ListHead* > p;
    if(ListHead.size()==0)
        return NULL;
    for(int i=0;i<ListHead.size();i++)
    {
        p.push_back(ListHead[i]);
        if(max<ListHead[i]->val)
            max=ListHead[i]->val;
    }
    make_heap(p.begin(),p.end(),comp);
    ListNode* head=NULL, tail;
    while(1)
    {
        if(AtLeastOnePointNULL())
            break;
        if(p[0]->val<max)
        {
            while(p[0]->val<max)
            {
                pop_heap(p.begin(),p.end());
                ListNode* pnext=p[p.size()-1]->next;
                if(pnext==NULL)
                    return head;
                if(max<pnext->val)
                    max=pnext->val;
                p.pop_back();
                p.push_back(pnext);
                push_heap(p.begin(),p.end());
            }
        }
        else
        {
            if(head==NULL)
            {
                head=new ListNode(p[0]->val);//at least one ele in heap
                tail=head;
            }
            else
            {
                tail->next=new ListNode(p[0]->val);
                tail=tail->next;
            }
            for(int i=0;i<p.size();i++)
            {
                p[i]=p[i]->next;
                if(p[i]->val>max)
                    max=p[i]->val;
            }
            make_heap(p.begin(),p.end(),comp);
        }
    }
    return head;
}

Posted by richard爱闹 - 8月 23 2014
如需转载,请注明: 本文来自 Richard

latex notes

加上删除线,加上包\usepackage{ulem}
\sout{}, 但是会重定义\emph 为下划线,到时候是会去掉这些删除线的,所以也没必要取消这种重定义

http://blog.sina.com.cn/s/blog_676069b10101jrob.html 重定义dvi2pdf快捷键,和dvips冲突,把这个注释掉,因为不怎么用到

Latex Ctrl+Shf+L
dvi2odf Ctrl+Shf+D

现在有一个问题就是源基因组这个work的数据和文章非常混乱,数据需要再整理一遍。要花时间整理一下,就像数据一样,整理不好的话查找比较费时,要用好的DS来存储

C输入,上次瞿神说的scanf %s 不接受空格 结束符理解出错了,以为是结束符可以处理空格。
后来发现又遇到错误了,scanf()%s 这么解释:如果遇到whitespace 直接跳过,知道第一个合法字符串,然后遇到第一个whitespace结束,whitespace留在缓冲区内,
其实。也就是说scanf(“%s” ) cin输入字符串的时候,必须读到non-whitespace 才罢休,否则一直准备输入,然后你一直敲换行符。而其他的getline gets fgets之类的则会一遇到whitespace就接受,然后认为读完了。所以在第一行n,后面是一行带空格的字符串需要一次读入的时候,必须要处理缓冲区中多出的\n, 瞿神
用getchar, 当然任何一遇到whitespace就结束的 gets getline之类的都是可以的,也都是等价的,然后后面用getline gets之类的就可以了。

正如fawkes大神,scanf gets来输入字符串足矣,scanf(“%s”)处理whitespace分隔的字符串,gets输入一行,但是可能会有缓冲区溢出的风险,用fgets会比较安全,因为指定了字符串的
大小,但是一般都不会太讲究字符串大小,但是fgets会多加一个\n, 所以要当心,所以还是gets足以了。一般自己小的程序自己控制好没有什么风险。但是对于处理代码注释的那道题来说刚好合适,因为正好要需要记录每行的换行符。

所以scanf(“%s”) cin不接受whitespace作为读入的字符串,必须读到non-white才罢休,然后以后面的whitespace结束, 所以这种不要
处理Notorious的cin因为只peek 输入缓冲区的whitespace而留在那里的whitespace, 而其他的例如 scanf(%c) getline都会接受的。
因此int 换行加字符串的问题用scanf(%s), cin不需要额外处理那个多出的\n, 但是如果要读一整行数据这个又不行,必须用gets之类的,因此又要处理\n, 只能getchar之类的。
这种情况反而出现的非常多的。

然后就是strcmp函数比较两个字符串大小,刚好符合题意,a>b, strcmp(a,b)>0, a=b strcmp(a,b)=0, a<b strcmp(a,b)<0
其实相对还是cpp的string更熟悉些,封装比较好,也方便,但是C的效率高,因为叫底层。

今天瞿神也说了strcpy不如memcpy效率高,因为后者是直接操作内存的字节数。但是要加一个参数表示copy的字节数,这样其实更容易清楚整个过程。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
using namespace std;
char soulang[95],tarlang[95];
char numstr[1000];
char result[1000];
int T;
int main()
{
    //freopen("in.txt","r",stdin);
    scanf("%d",&T);
    int casei=1;
    while(T--)
    {
        scanf("%s%s%s",&numstr,&soulang,&tarlang);
        int numlen=strlen(numstr),soulen=strlen(soulang),tarlen=strlen(tarlang);
        int numdig=0,wei=numlen-1;
        for(int i=0;i<numlen;i++)
        {
            for(int j=0;j<soulen;j++)
            {
                if(numstr[i]==soulang[j])
                {
                    numdig+=j*pow((double)soulen,numlen-i-1);
                    //wei--;
                    break;
                }
            }
        }
        //result="";
        int resulti=0;
        //cout<<"numdig: "<<numdig<<endl;
        if(numdig==0)
            result[0]=tarlang[0],result[1]='\0',resulti=1;
        else
        {
        while(numdig!=0)
        {
            result[resulti]=tarlang[numdig%tarlen];
            numdig/=tarlen;
            resulti++;
        }
            result[resulti]='\0';
        }
        for(int i=0;i<resulti/2;i++)
        {
            char tmp=result[i];
            result[i]=result[resulti-i-1];
            result[resulti-i-1]=tmp;
        }
        printf("Case #%d: %s\n",casei++,result);
    }
    return 0;
}

#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
using namespace std;

char str[100+1],laststr[100+1];
int T,n,cost;
bool bigger()
{
    return strcmp(str,laststr)>0;
}

int main()
{
    freopen("in.txt","r",stdin);
    scanf("%d",&T);
    int casei=1;
    while(T--)
    {
        scanf("%d",&n);
        //gets(laststr);
        getchar();
        cost=0;
        for(int i=0;i<n;i++)
        {
            if(i==0)
                //scanf("%s",laststr);
                gets(laststr);
            else
            {
                //scanf("%s",&str);
                gets(str);
                //printf("%s\n",str);
                if(bigger())
                    strcpy(laststr,str);
                else
                    cost++;
            }
        }
        printf("Case #%d: %d\n",casei++,cost);
    }
    return 0;
}

#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <vector>
using namespace std;

int a[1000];
vector<int> ji,ou;
int T,n,x;
void bubble()
{
    //for(int i=0;i<ou.size();i++)
    //    cout<<ou[i]<<" ";
    //for(int i=0;i<ji.size();i++)
    //    cout<<ji[i]<<" ";    
    for(int i=0;i<(int)ou.size()-1;i++)
    {
        for(int j=0;j<(int)ou.size()-i-1;j++)
        {
            if(a[ou[j]]<a[ou[j+1]])
                swap(a[ou[j]],a[ou[j+1]]);
        }
    }
    for(int i=0;i<(int)ji.size()-1;i++)
    {
        for(int j=0;j<(int)ji.size()-i-1;j++)
        {
            if(a[ji[j]]>a[ji[j+1]])
                swap(a[ji[j]],a[ji[j+1]]);
        }
    }
}

int main()
{
    //freopen("in.txt","r",stdin);
    scanf("%d",&T);
    int casei=1;
    while(T--)
    {
        ji.clear();
        ou.clear();
        scanf("%d",&n);
        for(int i=0;i<n;i++)
            scanf("%d",&a[i]);
        for(int i=0;i<n;i++)
        {
            if(a[i]%2==0)
                ou.push_back(i);
            else
                ji.push_back(i);
        }
        bubble();
        printf("Case #%d:",casei++);
        for(int i=0;i<n;i++)
            printf(" %d",a[i]);
        puts("");
    }
    return 0;
}

comp函数这么理解,bool comp(int i, int j) 表示是否i 应该放在j前面,return i<j 表示i<j时,i在j前面,所以是非递减排列。

A题需要估算最大的整数的字符串长度,当进制2位,log2(10^9) 也就是10亿对数,10^9<1G=2^30, 因此log数小于30的。1G略大于10亿,int的最大值就是2^31-1, 也即21开头
的10位十进制数。

ACII码先大写,后小写,strcmp(“A”, “a”)=-32; 因为A比a小

今天写sort的时候,遇上了 从未遇到的问题,comp函数调用不需要加(), 而是直接函数名,太久没写sort了。

后来发现这么一个定制的排序的comp函数似乎不好写,因为sort的comp函数 不允许修改传入的参数,因此对于参数为引用传递直接报错,以此防患于未然。
但是我设计的是有swap的,因此就跪了。

bool asc( pair<int, int> i, pair<int, int> j)
{
    if(i.first<j.first)
        return true;
    else
    {
        swap(i.second, j.second);
        return false;
    }
}

bool desc( pair<int, int> i, pair<int, int> j)
{
    if(i.first>j.first)
        return true;
    else
    {
        swap(i.second, j.second);
        return false;
    }
    //return ou[i].first>ou[j].first;
}

void bubblelib()
{
    sort(ji.begin(),ji.end(), asc);
    //cout<<"ji:"<<endl;
    //for(int i=0;i<ji.size();i++)
    //    cout<<ji[i].first<<":"<<ji[i].second<<" ";
    sort(ou.begin(),ou.end(),desc);
    //cout<<endl<<"ou:"<<endl;
    //for(int i=0;i<ou.size();i++)
    //    cout<<ou[i].first<<":"<<ou[i].second<<" ";

    for(int i=0;i<ji.size();i++)
        a[ji[i].second]=ji[i].first;
    for(int i=0;i<ou.size();i++)
        a[ou[i].second]=ou[i].first;

}

群里说有点像Shell排序
http://acdream.info/contest?cid=1095#problem-D

今天print发现offline了,其他人可以打印,于是发现自己的问题,想到这个是网络打印机,从网络里面连上的,可能和之前的不一样。于是发现是自己断过网,但是重新连出现
http://article.pchome.net/content-554834.html
于是重启了print服务就可以了。

之前admis的添加打印机方式不会出现断网后重新连的问题,NTU那边也是这么练的,不会出现问题,难道是本地打印机的原因,而这个是网络打印机。不太清楚。。。。

Posted by richard爱闹 - 8月 22 2014
如需转载,请注明: 本文来自 Richard

2048

这道题目是Google2015笔试题了最简单的一道了,但是似乎也要考虑清楚情况,

先做一个方向,调好之后其他的只要相应的改就可以了。

如果right的话,需要从后向前递推,因为题目一个比较奇怪的要求,2 2 2 right就会0 2 4, 所以不能left到right直接过来,不然要出错了,只能从后面向前腿,
但是找合并的顺序还是向right。

如果0不做,否则,先往前找最长的0,然后看是否合并,要这个数之前未合并,并且数和当前相等。同时注意右移index越界的情况。然后exit while循环注意分条件。

一开始做的时候还考虑了合并之后的合并,其实是没看清楚题意,一定要审清楚题意再开始写,非常重要,不然简单的题反而想复杂,非常不划算。

#include <cstdio>

int T,n,a[20][20],merge[20][20];
char str[10];
void right()
{
    int start;
    for(int i=0;i<=n-1;i++)
    {
        for(int j=n-2;j>=0;j--)
        {
            if(a[i][j]==0)
                ;
            else
            {
                start=j+1;
                while(start<=n-1 && a[i][start]==0)
                    start++;
                if(start>=n)
                {
                    a[i][n-1]=a[i][j];
                    a[i][j]=0;
                }
                else
                {
                    if(a[i][start]==a[i][j])
                    {
                        if(merge[i][start]==false)
                            a[i][start]*=2,a[i][j]=0,merge[i][start]=true;
                        else
                            a[i][start-1]=a[i][j],a[i][j]=0;
                    }
                    else
                    {
                        if(start!=j+1)//start modified
                            a[i][start-1]=a[i][j],a[i][j]=0;
                    }
                }
            }
        }
    }
}
void left()
{
    int start;
    for(int i=0;i<=n-1;i++)
    {
        for(int j=1;j<=n-1;j++)
        {
            if(a[i][j]==0)
                ;
            else
            {
                start=j-1;
                while(start>=0 && a[i][start]==0)
                    start--;
                if(start<0)
                {
                    a[i][0]=a[i][j];
                    a[i][j]=0;
                }
                else
                {
                    if(a[i][start]==a[i][j])
                    {
                        if(merge[i][start]==false)
                            a[i][start]*=2,a[i][j]=0,merge[i][start]=true;
                        else
                            a[i][start+1]=a[i][j],a[i][j]=0;
                    }
                    else
                    {
                        if(start!=j-1)//start modified
                            a[i][start+1]=a[i][j],a[i][j]=0;
                    }
                }
            }
        }
    }
}
void down()
{
    int start;
    for(int j=0;j<=n-1;j++)
    {
        for(int i=n-2;i>=0;i--)
        {
            if(a[i][j]==0)
                ;
            else
            {
                start=i+1;
                while(start<=n-1 && a[start][j]==0)
                    start++;
                if(start>=n)
                {
                    a[n-1][j]=a[i][j];
                    a[i][j]=0;
                }
                else
                {
                    if(a[start][j]==a[i][j])
                    {
                        if(merge[start][j]==false)
                            a[start][j]*=2,a[i][j]=0,merge[start][j]=true;
                        else
                            a[start-1][j]=a[i][j],a[i][j]=0;
                    }
                    else
                    {
                        if(start!=i+1)//start modified
                            a[start-1][j]=a[i][j],a[i][j]=0;
                    }
                }
            }
        }
    }
}

void up()
{
    int start;
    for(int j=0;j<=n-1;j++)
    {
        for(int i=1;i<=n-1;i++)
        {
            if(a[i][j]==0)
                ;
            else
            {
                start=i-1;
                while(start>=0 && a[start][j]==0)
                    start--;
                if(start<0)
                {
                    a[0][j]=a[i][j];
                    a[i][j]=0;
                }
                else
                {
                    if(a[start][j]==a[i][j])
                    {
                        if(merge[start][j]==false)
                            a[start][j]*=2,a[i][j]=0,merge[start][j]=true;
                        else
                            a[start+1][j]=a[i][j],a[i][j]=0;
                    }
                    else
                    {
                        if(start!=i-1)//start modified
                            a[start+1][j]=a[i][j],a[i][j]=0;
                    }
                }
            }
        }
    }
}

void Show()
{
    for(int i=0;i<n;i++)
    {
        printf("%d",a[i][0]);
        for(int j=1;j<n;j++)
        {
            printf(" %d", a[i][j]);
        }
        puts("");
    }
}
void init()
{
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<n;j++)
            merge[i][j]=false,scanf("%d",&a[i][j]);
    }
}


int main()
{
    freopen("B-large-practice.in","r",stdin);
    freopen("B-large-practice.out","w",stdout);
    scanf("%d",&T);
    int casei=1;
    while(T--)
    {
        scanf("%d%s",&n,&str);
        init();
        if(str[0]=='r')
        {
            right();
            printf("Case #%d:\n",casei++);
            Show();
        }
        else if(str[0]=='l')
        {
            left();
            printf("Case #%d:\n",casei++);
            Show();
        }
        else if(str[0]=='d')
        {
            down();
            printf("Case #%d:\n",casei++);
            Show();
        }
        else if(str[0]=='u')
        {
            up();
            printf("Case #%d:\n",casei++);
            Show();
        }
    }
    return 0;
}

附上一位童鞋的代码 http://blog.csdn.net/xhu_eternalcc/article/details/38667641

Posted by richard爱闹 - 8月 21 2014
如需转载,请注明: 本文来自 Richard

MergingSets

一般的并查集只有find,merge两种,写一个最简单的版本代码可以非常短,find包括递归 非递归,普通和压缩路径这么四种,据瞿神所说递归
版的效率不会差到哪儿去,merge又分平衡版和非平衡版的merge

初始用-1表示集合头,否则p[x]表示x的所在树形结构的父亲所在的index, -1表示是集合头
普通查找

int find(int x)
{
    if(a[x]==-1)
        return x;
    else
        return find(a[x]);
}

带压缩路径的查找

int find(int x)
{
    if(a[x]==-1)
        return x;
    else
        return a[x]=find(a[x]);//瞿神指出了错误
}

void merge(int x, int y)
{
    int xi=find(x),yi=find(y);
    p[xi]=yi;
}

Posted by richard爱闹 - 8月 21 2014
如需转载,请注明: 本文来自 Richard

Class Constructor

今天翻阅了一下C++书,回忆起当初Class的四大组成部分,构造,析构,复制构造和赋值运算符函数。

并从一个string类的设计来用5个例子阐述,如何一步步完成带有heap空间的string类的设计,最初用字符数组有空间大小的局限,改为new之后
还会有共享资源错误,丢失资源指针等错误,大为所快!

记得剑指Offer看到复制构造函数必须参数为引用传递,否则会出现无穷递归调用复制构造函数情况,因为传入参数的过程也会调用复制构造函数,如果是值传递的话。

浅复制是直接修改指针为原资源,这样在析构的时候第二次会因为空间已释放二出错,但是这样还不够,当出现赋值的时候,默认的赋值运算符函数会直接申请新的空间,会丢失
原有的堆空间,所以需要做四件事情,也即释放原有空间,申请新的空间,拷贝内容,返回this, 因为这是一个operator函数,除了构造,复制构造,析构函数以外,似乎都需要
return
this指针,因为需要返回一个对象类型的数据。

还记得之前写Leetcode由于不习惯写class,只写个面向过程,最后sort的comp()函数 始终编译出错,因为这种需要static, 因为non-static 都是每个对象一份,会用*this来调用
这个参数,隐藏在第一个参数里面。

还记得chenshuo大神,作为资深C++ developer,写了一个较简洁高端的string class的实现。

还有构造函数冒号语法会按照后面的顺序而不是定义的成员变量的顺序来初始化,二每个对象都是调用各自对象的构造函数的。

由于chenshuo大神说道,面试经常让你最快速度实现一个string类的设计,因此有了以下代码,尽管字符串常量传入会有问题,因为指向常量后,str3的字符串就不能修改内容了。

#include <iostream>
#include <cstdio>
using namespace std;
class String
{
    public:
        String(const char* pstr="")
        {
            str=new char[strlen(str)+1];
            strcpy(str,pstr);//auto add \0
        }
        String(const String& str1)//must be ref, val will called non-limited recursive
        {
            str=new char[strlen(str1.str)+1];
            strcpy(str,str1.str);
        }
        ~String()
        {
            if(str!=NULL)
                delete[] str;
        }
        String& operator=(const String& str1)
        {
            if(str != str1.str)
            {
                delete[] str;
                str=new char[strlen(str1.str)+1];
                strcpy(str,str1.str);
            }
            return *this;
        }
        void modify(char* ptr)
        {
            str=ptr;
        }
        void Show()
        {
            printf("%s\n",str);
        }
    private:
        char* str;
};

int main()
{
    String str1("zrc");
    String str2(str1);
    String str3=str1;
    str3.modify("wch");
    str1.Show();
    str2.Show();
    str3.Show();
    return 0;
}

代收邮件,以为gmail代收了NTU邮箱,后来发现其实是NTU转发到gmail

后来发现puts 函数其实是自动append\n 的,以为只是输出一个字符串常量

Posted by richard爱闹 - 8月 21 2014
如需转载,请注明: 本文来自 Richard

BT based DP for max score

之前只做过一道基于BT的DP,还是瞿神一起讨论的,大学四年真的是代码写的太少。。。。

之前一道是计算BT里的最大pathsum, 任意起点终点,类似于记忆话存储,由于自己BT总是陷入指针,因此代码写的很繁琐,又是后续遍历推进,
又是层序遍历计算index,其实很多都可以缩减。

这道也是一样,https://vijos.org/p/1100
初看以为两边子树互相影响,然后不是DP,但是后来adhoc提示到左右子树都是连续的,因此可以DP,暂时不需要考虑树的样子。
DP方程 F(i,j)=max (F(i,k-1)*F(k+1,j)+A[k]) , i+1<=k<=j-1

只要重叠子问题首选DP,正如瞿神所说。后面再类似矩阵连s[i][j]记录断链位置,此处是根的位置,然后用递归构造先序遍历

#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
using namespace std;

typedef long long LL;

LL dp[30+1][30+1];
int s[30+1][30+1];
LL A[30+1];
vector<int> PreVec;
int n;
LL btmaxscore()
{
    //int length;
    for(int i=1;i<=n;i++)
        dp[i][i]=A[i],s[i][i]=i;
    //for(int i=1;i<=n-1;i++)
    //    dp[i][i+1]=1;
    for(LL length=2;length<=n;length++)
    {
        for(LL i=1;i<=n;i++)
        {
            LL j=i+length-1,max;
            max=1*dp[i+1][j]+A[i];s[i][j]=i;
            if(max<dp[i][j-1]*1+A[j])
                max=dp[i][j-1]*1+A[j],s[i][j]=j;
            for(LL k=i+1;k<=j-1;k++)
            {
                if(max<dp[i][k-1]*dp[k+1][j]+A[k])
                    max=dp[i][k-1]*dp[k+1][j]+A[k],s[i][j]=k;
            }
            dp[i][j]=max;
        }
    }
    /*
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            cout<<dp[i][j]<<" ";
        }
        cout<<endl;
    }*/
    return dp[1][n];
}
void PreOrder(int i, int j)
{
    if(i<=j)
    {
        PreVec.push_back(s[i][j]);
        PreOrder(i,s[i][j]-1);
        PreOrder(s[i][j]+1,j);
    }
}
int main()
{
    //freopen("in.txt","r",stdin);
    //int n;
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=1;i<=n;i++)
            scanf("%I64d",&A[i]);
        LL maxscore=btmaxscore();
        PreVec.clear();
        PreOrder(1,n);

        printf("%I64d\n%d",maxscore,PreVec[0]);
        for(int i=1;i<PreVec.size();i++)
            printf(" %d",PreVec[i]);
        printf("\n");
    }
    return 0;
}

Posted by richard爱闹 - 8月 20 2014
如需转载,请注明: 本文来自 Richard

malloc

malloc函数大家都用的很多,但是具体实现可能确实没怎么考虑。

void *malloc(size_t size)

他需要做的事情和要求如下:
1.分配一段size个Bytes大小的堆空间(后来才知道是至少size大小),并返回一个void*类型的指针,指向这段空间的首地址。如果分配失败,返回NULL
2.每次分配的时候,必须是合法的地址空间,也即如何保证不会是之前分配过的。
3.便于free释放,因此必须free时候需要知道size大小,也即要和free realloc等配套使用,因此设计的DS必须兼顾

假设一段heap空间,Linux有一个break指针记录之前已经分配的空间的高地址,再高的部分就是非法地址了。因此一个toy的malloc可以借助sbrk函数来实现,

int brk(void addr);
void
sbrk(intptr_t increment);

第一个函数将break上移increment字节大小,然后返回break之前的地址

这样借助sbrk函数可以完成,但是不好free因为没有记录这些大小,以及之前的首地址

#include <sys/types.h>
#include <unistd.h>
void *malloc(size_t size)
{
    void *p;
    p = sbrk(0);
    if (sbrk(size) == (void *)-1)
        return NULL;
    return p;
}

然后考虑使用链表来实现空间分配,将malloc的空间练成linklist,因为频繁插入删除,比数组好(我之前也觉得链表没啥用的,可以看下,这个是OS的实现)
然后首次适应和最佳适应的策略还历历在目,首次适应就需要split多余的空间提高空间利用效率,防止出现假满。

#define BLOCK_SIZE 24
void *first_block=NULL;

/* other functions... */

void *malloc(size_t size) {
    t_block b, last;
    size_t s;
    /* 对齐地址 */
    s = align8(s);
    if(first_block) {
        /* 查找合适的block */
        last = first_block;
        b = find_block(&last, s);
        if(b) {
            /* 如果可以,则分裂 */
            if ((b->size - s) >= ( BLOCK_SIZE + 8))
                split_block(b, s);
            b->free = 0;
        } else {
            /* 没有合适的block,开辟一个新的 */
            b = extend_heap(last, s);
            if(!b)
                return NULL;
        }
    } else {
        b = extend_heap(NULL, s);
        if(!b)
            return NULL;
        first_block = b;
    }
    return b->data;
}

以上几乎是码代码的张洋最新的blog笔记,新鲜出炉,学习一下,面试可能探讨会涉及~~~~
http://blog.codinglabs.org/articles/a-malloc-tutorial.html

Posted by richard爱闹 - 8月 19 2014
如需转载,请注明: 本文来自 Richard

Two Screens

今天下午看pdf感觉特别难受,鄙人喜欢双栏显示,17寸的字太小,Zhengjie那边都是21寸的屏幕,换成这个感觉有点小,
于是打算拿实验室最近弃置的显示屏,昨天翻CMU李沐的微博,很多都是3屏,还是多屏方便,应该早听一夫打算的。

显示屏有VGA DVI HDMI这几种实验室都是VGA DVI的,但是发现我的主机剩的接口是DVI,实验室弃置的似乎都是VGA,
DVI分了四种,DVI-I DVI-D各两种,DVI-D好像是由于缺少模拟信号,所以不能转VGA,二DVI-I的可以。而我的恰好是DVI-D的。。。

于是一种方案是解一个DVI-D的显示屏,但是实验室新的都已经投入使用了,除非我自己花钱买一个。。

要么试试VGA分屏器,后来发现分屏其实还是显示一个屏幕的内容,没啥意义。

就在此时,我发现里面还有一个支持DVI-D和VGA的屏幕,似乎是邵师姐的,于是拿来,借if大神的线发现可以,一阵惊喜,下单买DVI-D即将体验双拼的快乐~~~

Posted by richard爱闹 - 8月 19 2014
如需转载,请注明: 本文来自 Richard

LDA notes

每次再看LDA,都要费一番功夫,因此需要记录一些比较接地气的笔记外加形象的比喻。让一个CS的人去弄LDA似乎有点吃别人饭碗的感觉,这块几乎是
数学或者说统计知识,当然可能某些CS背景的Math基础较好的学生可能看起来也不是太费力了。

包括模型的过程(其他地方可能是目标函数,但是这里是没有的,因为目的是为每一篇文档计算一个主题分布,至少我用来做元基因组聚类只需要这个东西)

  1. Dirichlet
  2. Multinomal
  3. Dirichlet
  4. Multinomial

正如Ahdoc大神说的,LDA牵涉的数学先验知识太多了,一个CS背景的人几乎是不太可能对他加以改进的,所以我也只能把抽象的语言稍加形象解释。

1.Dirichlet是多维连续变量分布(概率论课虽然学的是A,但是也只讲了二维的,课本二维推广到高维没有实质困难这句话将困难都留给我等CS学生了),参数是一个K
维向量,alpha1….alphaK, 并且概率密度函数有一个belta函数,暂时不管他长啥样。

因此从一个概率分布里抽样,其实是获取一次X的值,对于多维就是一个K维向量,第一步的Dirichlet是K维,K表示topic数,这是用户无背景知识输入的,因此具有主观性,最后
的结果也是通过这个来调节 ,记得之前微博说如果十二神肖的topic改为11会变成什么样。。。

抽样得到了K维向量,表示第i个文档的主题分布,因为X1+….XK=1, 刚好符合主题概率和为1。

2.第二步是获取第i个文档j个单词的具体主题,从多项分布中抽样获取一个具体的值(主题table里的index,具体编程实现),P(X1=N1,X2=N2…Xk=Nk)=(N!/(N1!…Nk!))p1^N1*p2^N2…pk^Nk,这里表示一次抽样有K种结果,那么抽样N次,Xi结果又Ni次的概率由上式表示。

因此很多地方对于这里MultiNomial分布产生质疑,因为其实N=1,准确的说是Categori分布,也就是N=1的多项分布,有一个K维的参数可以抽样得到一个具体的值(根据概率),犹记得看了
sumous_t的随机数笔记后,对于这方面的编程实现有了一定的了解,可以稍微帮助理解这一过程以及编程实现。

3.还是一个Dirichlet,有时称职为分布上的分布,但是这次不是K维而是V维的了,V表示单词vocabular的大小,所以同上可以抽样得到V维的一个向量,表示之前算出的主题对应的
单词分布,也是概率和为1,理论上来说,实际不是,Blei的代码用了对数处理,具体原因暂不明确。

4.同上还是MultiNomial分布,然后根据公式,抽样出一个具体的单词出来。

关于求解有VB(基于著名的EM算法),以及gibbs sampling, 对于gibbs sampling,抽样就是通过多次采样来逼近实际的结果。但是Wikipedia上的说明似乎看的不太明白。

现在GS算法较普遍,求解简单,并且利于并行,尽管前后两次采样的依赖关系影响了更高层次的并行。

Dirichlet http://zh.wikipedia.org/wiki/%E7%8B%84%E5%88%A9%E5%85%8B%E9%9B%B7%E5%88%86%E5%B8%83
B http://zh.wikipedia.org/wiki/%CE%92%E5%88%86%E5%B8%83
http://en.wikipedia.org/wiki/Latent_Dirichlet_allocation

http://zxw.xjxnw.com/ZCL/part2/C11g.htm MultiNomial分布

PS:Lab里都是教育网,还push不到github,这有点悲催,还要VPN,但是设置Library代理好像也不行,不知道是不是系统代理对git无效。

记得之前MultiNomial分布的参数写错了,是N维,而不是Ni维的。

今天弄完Gibbs Sampling,累觉不爱了。。。。。

Posted by richard爱闹 - 8月 19 2014
如需转载,请注明: 本文来自 Richard

shadowsocks

了解到现在翻墙改的hosts各种逐渐被查,goagent freegate也是类似的原理,都不是长远之计,而且之前用的
goagent感觉速度很慢,后来用了YJY和MHG两位大神搭建的VPS,再次感谢,速度非常之快。

Windows的话,本地run一个local的app,有GUI版的,配置一下server IP server port, local port等,
然后浏览器设置本地socket v5代理,127.0.0.1, 浏览器先连local app,通过local app连接到VPS,然后翻墙,本质也是
使用国外的VPN上网。

app download(windows C# depreated了,直接用GUI版的) https://github.com/shadowsocks/shadowsocks-gui

教材:http://www.yaoblog.info/?p=7433

PS:复旦的有线网似乎连不上VPS,可能是教育网的原因,Wifi可以的,我用复旦的VPN也可以连。

今天打算26上配置下VIM,于是乎github连不上,悲催的教育网,而且设置了Linux代理也不行,export http_proxy 似乎无效,也可能是需要重启才能生效。

于是本地VPN下载了传到Linux,发现遇各种问题,因为~目录下的.vim开头的文件或目录都是隐藏文件,不能显示,而且pscp直接传送目录似乎权限也不够,远程创建可能不行,只能打包
之后解压缩,但是还是报了错误

liaorq@admis502:~/algcode/maxxor$ sh ~/.vim_runtime/install_awesome_vimrc.sh     
cd: 1: can't cd to /home/liaorq/.vim_runtime
: not foundq/.vim_runtime/install_awesome_vimrc.sh: 2: 
: not foundq/.vim_runtime/install_awesome_vimrc.sh: 20: 
Installed the Ultimate Vim configuration successfully! Enjoy :-)

目前还没解决,就先暂时这样吧。。。。

Posted by richard爱闹 - 8月 18 2014
如需转载,请注明: 本文来自 Richard