shared_ptr

给定N个直线,问最多将一个平面划分为几个区域,fn=fn-1+n, f1=1; 按照递推 fn=1+Sn
给定N个折线,只有一半,划分为几个区域,gn=f2n-2n, 因为对于每个折线,区域从原来4个变为2,损失2,

C++STL sort 解决最坏情况就是对于一般的进行快速排序,如果数据量特别大,则如果递归层次过大,采用heap sort来排序

mark一个Latex错误,如果出现while converting from UNICODE to CP 0 错误
那么需要将.tex文件先转为UTF-8编码,可以用Notepad++转,然后用WinEdt打开

shared_ptr用法。
赋值运算符函数,直接上代码: )

r->left=make_shared<Node>(); 

这段调用了make_shared来构造一个临时的shared_ptr对象,其实SP就是封装了指针,使得内存使用安全的类,这里会调用Node的构造函数,然后我们定义在这里。其他用法都不对,例如
r->left=new Node(), 因为Node*不能赋值给shared_ptr, r->left(new Node), 这种也不行,因为这里不是构造函数,而是赋值运算符函数。

struct Node
{
int value;
shared_ptr left,right;
Node() : value(INT_MIN), left(NULL), right(NULL){}
};

用了shared_ptr就记住不要有*,这是cpp最带刺的地方。

顺便附上一个 算法黑书输入颗二叉树,输出层序遍历的代码。如果构造出错的,例如内部点没赋值,或者一个点出现多次,都输出-10

input:
(3,L) (4,R) (2,) ()
(11,LL) (7,LLL) (8,R) (5,) (4,L) (13,RL) (2,LLR) (1,RRR) (4,RR) ()

#include <iostream>
#include <cstring>
#include <string>
#include <queue>
#include <cstdio>
#include <memory>
#include <climits>
using namespace std;

struct Node
{
    int value;
    shared_ptr<Node> left,right;
    Node() : value(INT_MIN), left(NULL), right(NULL){}
};
shared_ptr<Node> root;
int stoi(string x)
{
    int sum=0;
    for(auto e: x)
        sum=sum*10+e-'0';
    return sum;
}
bool invalid;
void addNode(string s,int v)
{
    int len = s.length();
    shared_ptr<Node> r = root;
    for(auto e:s)
    {
        if(e == 'L'){
            if(r->left == NULL) r->left=make_shared<Node>();
            r = r->left;
        }
        else if(e == 'R'){
            if(r->right == NULL) r->right=make_shared<Node>();
            r = r->right;
        }
    }
    if(r->value==INT_MIN)
        r->value = v;
    else invalid=1;
}

int main()
{
    invalid = 0;
    string str;
    root = make_shared<Node>();
    while(cin>>str)
    {
        int len = str.size();
        str = str.substr(1,len-2);
        int pos = str.find(",");
        if (pos == -1) break;
        int val = stoi(str.substr(0,pos));
        string dir = str.substr(pos+1, str.size()-pos-1);
        addNode(dir,val);
    }
    queue<shared_ptr<Node>> q;
    vector<int> ans;
    q.push(root);
    while(!q.empty())
    {
        auto cur=q.front();q.pop();
        if(cur->value!=INT_MIN) ans.push_back(cur->value);
        else
        {
            invalid=1;
            break;
        }
        if(cur->left) q.push(cur->left);
        if(cur->right) q.push(cur->right);
    }
    if(invalid) cout<<"-1"<<endl;
    else
    {
        for(auto e: ans) cout<< e << " ";
        cout << endl;
    }
    return 0;
}

Posted by richard爱闹 - 3月 13 2015
如需转载,请注明: 本文来自 Richard

Ball Down

算法黑书上一道题目,题意是深度为D的满二叉树,内部节点有开关,初始关,每个小球从root开始下降,如果关向左,否则向右,同时开关切换状态

我找了一下规律,发现二进制表示比较明显,
0000
1000
0100
1100
0010
1010
0110
1110

第一位是0101出现,第二位是0011出现,第三位是00001111出现,如此下去,因此有如下代码:

while(cin>>D>>I)
{
    I--;
    int bit, sum=0;
    for(int k=1;k<=D-1;k++)
        bit=I%(1<<k)/(1<<(k-1)), sum=sum*2+bit;
    sum+=(1<<(D-1));
    cout<<sum<<endl;
}

Posted by richard爱闹 - 3月 10 2015
如需转载,请注明: 本文来自 Richard

GCD application

这道题目 如果是 搜索做,主要一个限制条件判断就是A1*…Ak是否是M整数倍,这里看到某大神代码用了这样
一个递推关系来判定,解决了大整数 溢出的问题

P(i)=(j=i->n) GCD(P(i-1)j, m), 最后判P[k]==m 等价于 A1…Ak是否是M的倍数。
具体的证明都是马后炮了,在限时的比赛里几乎没用,重要的是会有一种称为数感的东西,大致是这样,然后
模拟点数据验证一下。

试着推导了一下,P(i)=gcd(P(i-1)Ai, m)=….=gcd(A1,m)….gcd(Ak,m)
而gcd(A1,m)….gcd(Ak,m)<====>A1…Ak%m==0

gcd有如下性质
gcd(a, b)<=min(a, b)
gcd(ab, c)=gcd(a, c)gcd(b, c)

具体代码如下,自己不知道这个方法的时候,做了质因数分解,然后hash存,每次还要hash减,dfs调用后还要复原,又称为回溯
这题有个DP做法更优,但是先实现这个再说

int n,m;
int solve(int now_pos,int lft,int lcm){
    if(lft == 0){
        if(lcm == m)return 1;
        return 0;
    }
    int cnt=0;
    for(int i=now_pos;i<=lft;i++){
        cnt+=solve(i+1,lft-i,__gcd(lcm*i,m));
    }
    return cnt;
}

Posted by richard爱闹 - 3月 9 2015
如需转载,请注明: 本文来自 Richard

erase element during traversal

今天遇到一个问题就是遍历一个容器的时候删除应该怎么做。
这个其实是经常遇到的问题,但是没有总结。

如果C++11的话,非常方便,如下是C++ Primer推荐的方式(reference by 战神 and 炮哥)

for(map<int, int>::iterator it=hash1.begin();it!=hash1.end();)
{
    if(j%(it->f)==0 && (it->s)>=1)
    {
        it->s--;
        if(it->s==0)
        {
            it=hash1.erase(it);
            continue;
        }
    }
    it++;
}

因为删除后,当前迭代器失效了,然后返回下一个元素的迭代器,这样遍历正常运行,否则按照正常it++就好了 ,由于这个分治是两个if,因此代码用continue可能更方便些。

如果C++98的话,这种方式似乎是可以的,从博客 http://www.cnblogs.com/zhoulipeng/p/3432009.html

for(map<int, int>::iterator it=hash1.begin();it!=hash1.end();)
{
    if(j%(it->f)==0 && (it->s)>=1)
    {
        it->s--;
        if(it->s==0)
        {
            hash1.erase(it++);
            continue;
        }
    }
    it++;
}

主要是由于hihocode 不支持C++11,因此只能用C++98 的STL API,http://www.cplusplus.com/reference/map/map/erase/

另外还有一点就是关于continue的小细节
for(map::iterator it=hash1.begin();it!=hash1.end();)
{
if(j%(it->f)==0 && (it->s)>=1)
{
it->s—;
if(it->s==0)
{
it=hash1.erase(it);
continue;
}
}
it++;
}
如果it++放到for(;;)后面,那么continue的话,it++还是会执行的,所以要特别小心。

for(1;2;3){
  4
}

分组循环:[1,2] [4,3,2] [4,3,2]….
continue之后 由4跳到3。

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

CF269Div2ProC

这道题目乍一看以为是很复杂的搜索,搜索各种情况,就没有细想。其实只要找规律加枚举就好了,夫神比赛时间做出来,膜拜夫神。

http://codeforces.com/contest/471/problem/C

找到规律就好办了,每一层至少一个2,表示最左侧,然后右侧没加一个要加3,没保持上层比下层小,所以至少是0+1+…h-1个room, 每个room用3,
剩下的只要堆在最底层就可以,满足3的倍数,于是算法成型

LL Room(LL n)
{
    LL ans=0;
    for(LL i=0;;i++)
    {
        LL num=i*(i+1)/2;
        LL remain=n-(i+1)*2-num*3;
        if(remain<0) break;
        if(remain%3==0) ans++;
    }
    return ans;
}

时间复杂度是n=(i+1)/2+3/2*i*(i+1) 中i的结果

i=(sqrt(4+24n)-4)/6=O(sqrt(n))

时间复杂度是O(sqrt(n))

Posted by richard爱闹 - 3月 7 2015
如需转载,请注明: 本文来自 Richard

LIS application

http://codeforces.com/contest/4/problem/D

这道题目是一道LIS,后来也想到了,但是想dp[i]是以为尾元素为序列的最大值,从输入数据开始dp即使找到了最大值他的起点也未必是>w >h的,
然后觉得有点麻烦了,后来发现可以先把<=的都删掉,过滤一遍,这样剩下的任意一个值都是符合头的性质的,也就是严格的从头开始的递增序列。

但是WA了一次,发现dp之后构造解,忘记加递增的条件了(!j || (data[j-1].w<data[i-1].w && data[j-1].h<data[i-1].h)), 还漏了一次!j的判断,
这里其实因为只要返回任意解,直接for就可以解决了,如果是返回所有解,由于有多个分支,因此需要dfs每个分支了。

struct Node
{
    int w, h;
    int in;
    bool operator<(Node n) const
    {
        if(w!=n.w) return w<n.w;
        else return h<n.h;
    }
};
int main()
{
/*
#ifndef ONLINE_JUDGE
    freopen ("in.txt" , "r" , stdin);
    freopen ("out.txt" , "w" , stdout);
#endif
*/
    int n, w, h, w1, h1;
    cin>>n>>w>>h;
    int dp[n+1];
    vector<Node> data;
    for(int i=0;i<n;i++)
    {
        cin>>w1>>h1;
        if(w1>w && h1>h)
            data.push_back({w1,h1,i+1});
    }
    sort(data.begin(), data.end());
    n=data.size();
    dp[0]=0;
    int maxi=0, maxdp=dp[0];
    for(int i=1;i<=n;i++)
    {
        dp[i]=0;
        for(int j=0;j<i;j++)
        {
            if(!j || (data[j-1].w<data[i-1].w && data[j-1].h<data[i-1].h))
                dp[i]=max(dp[i], dp[j]+1);
        }
        if(dp[i]>maxdp) maxdp=dp[i], maxi=i;
    }
    vector<int> ans;
    for(int i=maxi;i>0;)
    {
        ans.push_back(data[i-1].in);
        int j;
        for(j=0;j<i;j++)
        {
            if(dp[i]==dp[j]+1 && (!j || (data[j-1].w<data[i-1].w && data[j-1].h<data[i-1].h)))
                break;
        }
        i=j;
    }
    reverse(ans.begin(), ans.end());
    cout<<ans.size()<<endl;
    for(auto e: ans) cout<<e<<" ";
    cout<<endl;
    return 0;
}

Posted by richard爱闹 - 3月 5 2015
如需转载,请注明: 本文来自 Richard

DFS color count

DFS题,但是犯了很多错误,http://codeforces.com/contest/505/problem/B
里面的图是多重边,每条边有color,保证两个点之间的边不会重色,也就是加一重循环,但是犯了很多错误,一直WA

  1. 对于输入点构建图忘记加上对立边了,例如mat[a][b]=c, mat[b][a]=c, 之前也翻过类似错误

  2. 对于dfs还是不熟练,炒肉大神建议局部变量传递,返回值设计,参数设计。关键是找到递归结构,结果找递归结构的时候已经看错题了。
    看成路径的个数,而不是color的种类数。int dfs, dfs(s,e)=sum(i:1->n && ) dfs(i,e), 这样之后就好写流程.

3.DFS框架是先判是否递归出口,也即找到目的点,如果不到终点,则判断是否已经访问过,当然也可以放在for循环里,但是这样结构清楚,
除非是必须比较当前与下一层的一个属性,然后又不加参数这样。这两个出口有时候可以互换,有时又不行,例如之前lc的一道word search,
自己第一遍写的时候老是死循环,后来G神帮我调代码就发现了问题,先判是否word到头了,再判是否访问过了。

  1. 由于需要判断当前颜色是否始终一致,如果从起点调用一次dfs,那么第一层的时候颜色是不需要判断的,因此还要设置变量来判断,这样很麻烦,
    于是for循环调用dfs,但是忘记设置v[s]=1

  2. visit 数组初始化位置不正确,放在每一个query开始

  3. 如果是需要遍历整个图的,需要再出口之后v[cur]=1, 同时最后return之前v[cur]=0, 这是中间没有return的情况,如果有的话,必须先v[cur]=0 复原,
    然后return ,否则有些状态变了返回到上一层的时候不能复原导致出错。

  4. 用一个color数组来存是否 计算过且有路径,有的话置为1, 本质和hash一样。

代码如下:

#include<set>
#include<map>
#include<list>
#include<cmath>
#include<queue>
#include<stack>
#include<ctime>
#include<cstdio>
#include<string>
#include<vector>
#include<climits>
#include<cstdlib>
#include<cstring>
#include<fstream>
#include<iostream>
#include<algorithm>
#include <unordered_set>
#include <unordered_map>
using namespace std;
const int maxn = 1e2 + 10;
typedef long long LL;
typedef unsigned long long ULL;
//int, -2^31~2^31-1    -2.1*10^9~2.1*10^9 (-2147483648-2147483647)
//unsigned int, 0~2^32-1  0~4.2*10^9
//long long  -2^63~2^63-1 -9*10^18~9*10^18
//unsigned long long 0~2^64-1  0~1.8*10^19
bool v[maxn];
bool validcolor[maxn];
int n;
vector<int> mat[maxn][maxn];
int dfs(int cur, int e, int color)
{
    if(cur==e) return 1;
    if(v[cur]) return 0;
    v[cur]=1;
    int sum=0;
    for(int i=1;i<=n;i++)
    {
        if(i==cur) continue;
        for(auto ele: mat[cur][i])
            if(ele==color && dfs(i, e, color)) {v[cur]=0; return 1;}
    }
    v[cur]=0;
    return 0;
}
int main()
{
/*
#ifndef ONLINE_JUDGE
    freopen ("in.txt" , "r" , stdin);
    freopen ("out.txt" , "w" , stdout);
#endif
*/
    int  m, q, qu, qv, x,y, c;
    for(int i=0;i<maxn;i++) for(int j=0;j<maxn;j++) mat[i][j].clear();
    cin>>n>>m;
    for(int i=0;i<m;i++) cin>>x>>y>>c, mat[x][y].push_back(c), mat[y][x].push_back(c);
    cin>>q;
    for(int i=0;i<q;i++)
    {
        cin>>qu>>qv;
        memset(validcolor, 0, sizeof validcolor);
        for(int i=1;i<=n;i++)
        {
            memset(v, 0 ,sizeof v);
            if(i==qu) continue;
            v[qu]=1;
            for(auto ele: mat[qu][i])
                if(!validcolor[ele] && dfs(i, qv, ele)) validcolor[ele]=1;;
        }
        int sum=0;for(int j=1;j<=m;j++) sum+=validcolor[j];
        cout<<sum<<endl;
    }
    return 0;
}

Posted by richard爱闹 - 3月 5 2015
如需转载,请注明: 本文来自 Richard

Valgrind Massif profiler

看到一篇文章用这个工具来查看内存,linux下安装,26设置了代理,/etc/apt/apt/conf.d/apt.conf文件里,设置了实验室代理,
但是注释掉似乎还是用不了163的源,安装这个工具,遂放弃。

  1. 可能源当前坏了
  2. 可能代理设置不止修改这个文件生效,因为看到网上是 /etc/apt/apt/apt.conf 这样一个路径
  3. 不知道了。

这个工具说看heap空间,但实际上也可以设置参数看stack空间占用

http://valgrind.org/docs/manual/ms-manual.html

Posted by richard爱闹 - 3月 4 2015
如需转载,请注明: 本文来自 Richard

push specific file types

这个问题很常见,因为一个目录下很多文件不是代码,尤其是做Programming Contest,输入输出之类的,因此需要上传指定文件。

git add *.cpp

这个是比较简单的做法,采用wildcard matching, 做了lc之后,就对正则有了新的认识。但是这个有几个缺点

  1. 可能不能递归到子目录
  2. 对于每个文件类型都要敲命令,例如有.h, .cpp等

最好的做法是 http://stackoverflow.com/questions/1139762/ignore-files-that-have-already-been-committed-to-a-git-repository
配置.gitignore文件,

*
!*/
!*.c
!*.h
!*.cpp
!*.java

其中!*表示子文件夹一样处理

这样可以解决上面两个问题

对于已经存在的repository,可以先删除,然后再重新push, 用以下命令组合

如果没有.gitignore文件,先touch .gitignore

git rm -r --cached .

git add .

git commit -m ".gitignore is now working"

git push -u origin master

…or create a new repository on the command line

echo # MasterThesis >> README.md
git init
git add README.md
git commit -m "first commit"
git remote add origin git@github.com:zhangruichang/MasterThesis.git
git push -u origin master

…or push an existing repository from the command line

git remote add origin git@github.com:zhangruichang/MasterThesis.git
git push -u origin master

…or import code from another repository

You can initialize this repository with code from a Subversion, Mercurial, or TFS project.

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

Combination DP

这道题目是APAC2015 RoundB 的ProblemA https://code.google.com/codejam/contest/4214486/dashboard,
也是NEU ACM的一道月赛题 http://acm.neu.edu.cn/hustoj/problem.php?id=1447

并且从这里学习了一下Stirling数,之前其实看过,是离散数学课跳过的内容,还有Catalan数,这两个组合数都是非常重要的,Catalan数
1/(n+1) C(2n, n) 是n对括号合法序列数,BOP上买票问题,二叉树个数等等重要的计数问题答案,Striling数相对运用不是特别广泛,
例如第一类是指一个n阶permutation可以由k个轮换乘积表示的个数,第二类是n个数划分为k个非空集合的划分方法,
二者都是通过递推公式来算的,当然也可以直接有对应的通项公式,但是非常复杂。

一开始看一篇博客,说的第一类Striling数,然后底下一哥们评论说是APAC这题,现在清楚了,他说错了,其实不是的,但是也是类似的递推公式。
DP其实就是递推,找出递推式,对于二维状态来说,多半是dp(i,j) 与dp(i-1, j), dp(i, j-1), dp(i-1, j-1) 发生纠葛,然后至于选哪几个,系数是多少
就是数学的brainstorm了,可以先看看子问题dp(i,j) 表示前j个位置放置i种数字的计数,前j-1个位置放置i种和i-1种, 如果放置了i种的话,最后一个任意放
i种的一种,idp(i,j-1), 如果是放置了i-1种数字,那么最后一个位置只能放置剩下的一种,但是前面i-1是i种里面的哪i-1种,C(i,i-1)=i, 所以idp(i-1,j-1)
因此得到dp(i,j)=i(dp(i-1,j)+dp(i-1,j-1)),i<j, i=1…m, j=1…n, m<=n, 需要设置base,dp(1,j)=1, dp(i,i)=i!,
注意对于这种溢出的情况,MOD一个大数,这个数是1e9+7,int范围内,但是(1e9+7)
100可能是超过int 范围的,因此用long long(10^18),递推式也不会超过LL范围,
因此代码如下:

LL dp[maxn][maxn];
const LL MOD=1e9+7;
int main()
{

#ifndef ONLINE_JUDGE
    freopen ("A-large-practice.in" , "r" , stdin);
    freopen ("A-large-practice.out" , "w" , stdout);
#endif

    LL t;
    cin>>t;
    for(LL ti=1;ti<=t;ti++)
    {
        LL m, n;
        cin>>m>>n;//m<=n
        for(LL j=1;j<=n;j++) dp[1][j]=1ll;
        for(LL i=2;i<=m;i++) dp[i][i]=(dp[i-1][i-1]*i)%MOD;
        for(int i=2;i<=m;i++) for(int j=i+1;j<=n;j++)
            dp[i][j]=(i*(dp[i][j-1]+dp[i-1][j-1]))%MOD;
        printf("Case #%d: ",ti);
        cout<<dp[m][n]<<endl;
    }

    return 0;
}  

代码如下,感觉这道是四次APAC ProblemA里最难的,其他的两道编程题,一道DFS,都比较make sense

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