valid rectangle

倒腾了半天,博客终于在admis也work了,距离上一篇似乎有些时日了刷些存在感。。。

输入四条用两个端点坐标表示的线段,判断是否为合法的面积大于0的矩阵

#include <stdio.h>
#include <algorithm>
using namespace std;
struct line
{
    int x1,x2;
    int y1,y2;
    bool nonlimitedk;
    double k;
    double b;
    line(): x1(0), y1(0),x2(0),y2(0),nonlimitedk(false),k(0)
    {
    }
};
line rec[4];
bool comp(line l1, line l2)
{
    if(l1.nonlimitedk==false && l2.nonlimitedk==false)
        return l1.k<l2.k;
    else if(l1.nonlimitedk==false && l2.nonlimitedk==true)
        return true;
    else if(l1.nonlimitedk==true && l2.nonlimitedk==false)
        return false;
    else
        return false;
}
bool quadrangle();
double Length(int i)
{
    return (rec[i].x1-rec[i].x2)*(rec[i].x1-rec[i].x2)+(rec[i].y1-rec[i].y2)*(rec[i].y1-rec[i].y2);
}

bool AllHasLength()
{
    for(int i=0;i<4;i++)
    {
        if(Length(i)<=0)
            return false;
    }
    return true;
}

int T;

bool paralle()
{
    if(AllHasLength()==false)
        return false;
    if(rec[0].nonlimitedk==false && rec[1].nonlimitedk==false && rec[2].nonlimitedk==false && rec[3].nonlimitedk==false)
    {
        if(rec[0].k==rec[1].k && rec[2].k==rec[3].k)
        {
            if(rec[0].k*rec[2].k==-1 && rec[0].b!=rec[1].b && rec[2].b!=rec[3].b && Length(0)==Length(1) && Length(2)==Length(3))
                return true;
            else
                return false;
        }
        else
            return false;
    }
    else if(rec[0].nonlimitedk==true && rec[1].nonlimitedk==true && rec[2].nonlimitedk==false && rec[3].nonlimitedk==false)
    {
        if(rec[2].k==rec[3].k && rec[2].k==0 && rec[2].b!=rec[3].b && rec[0].x1!=rec[1].x1 && Length(0)==Length(1) && Length(2)==Length(3))
            return true;
        else
            return false;
    }
    else if(rec[0].nonlimitedk==false && rec[1].nonlimitedk==false && rec[2].nonlimitedk==true && rec[3].nonlimitedk==true)
    {
        if(rec[0].k==rec[1].k && rec[0].k==0 && rec[0].b!=rec[1].b && rec[2].x1!=rec[3].x1 && Length(0)==Length(1) && Length(2)==Length(3))
            return true;
        else
            return false;
    }
    else if(rec[0].nonlimitedk==true && rec[1].nonlimitedk==true && rec[2].nonlimitedk==true && rec[3].nonlimitedk==true)
    {
        return false;
    }
    else
        return false;
}

bool rectangle()
{
    if(quadrangle()==false) return false;
    sort(rec,rec+4,comp);
    return paralle();
}
void Swap(line& l1,line& l2)
{
    line tmp;
    tmp=l1;
    l1=l2;
}

bool quadrangle()
{
    int curx,cury;
    curx=rec[0].x1;cury=rec[0].y1;
    for(int i=0;i<4;i++)
    {

        for(int j=i+1;j<4;j++)
        {
            if(rec[j].x1==curx && rec[j].y1==cury)
            {
                curx=rec[j].x2,cury=rec[j].y2;
                Swap(rec[j],rec[i+1]);
                break;
            }
            if(rec[j].x2==curx && rec[j].y2==cury)
            {
                curx=rec[j].x1,cury=rec[j].y1;
                Swap(rec[j],rec[i+1]);
                break;
            }
        }
    }
    if(curx==rec[0].x2 && cury==rec[0].y2)
        return true;
    else
        return false;

}


int main()
{
    freopen("in.txt","r",stdin);
    scanf("%d",&T);
    while(T--)
    {
        for(int i=0;i<4;i++)
            scanf("%d%d%d%d", &rec[i].x1,&rec[i].y1,&rec[i].x2,&rec[i].y2);
        for(int i=0;i<4;i++)
        {
            if(rec[i].x1!=rec[i].x2)//k != wuqiiong
            {
                rec[i].nonlimitedk=false;
                rec[i].k=double(rec[i].y2-rec[i].y1)/(rec[i].x2-rec[i].x1);
                rec[i].b=rec[i].x1-(double(rec[i].x2-rec[i].x1)*rec[i].y1/(double)(rec[i].y2-rec[i].y1));
            }
            else
            {
                rec[i].nonlimitedk=true;
                rec[i].b=rec[i].x1;
            }
        }
        if(rectangle())
            printf("YES\n");
        else
            printf("NO\n");
    }
    return 0;
}

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

Hello World

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in trobuleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment

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

BinaryPathSum

这道题目思路是瞿神提出的,瞿神能力确实很强,大一下,没有OI经历,算法已经非常出色了,小弟确实佩服,想当年小弟还在纠结于高数C++。。。
可见瞿神确实是付出了超出常人几倍甚至几十倍的时间,算法数据结构似乎很多方面比我还强。。。。

这道题目是Leetcode一道挺难的题目,找BT里面最长的path,任意两个点的,C(n,2)里面找,一个DP,后序顺序推进,保证孩子都计算出了,我的代码有点冗长,但是最后还是TLE了,具体原因不明,
后来发现没有记忆花存储,递归了,改了还是TLE。

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

#include "stdafx.h"
#include <iostream>
#include <map>
#include <stack>
#include <queue>
using namespace std;



class Solution {
    struct TreeNode {
      int val;
      TreeNode *left;
      TreeNode *right;
      TreeNode(int x) : val(x), left(NULL), right(NULL) {}
  };

public:
    TreeNode* root;
    int dp[10000];
    map<TreeNode*, int> nodemap;

    int maxPathSum(TreeNode *root) {
        if(root==NULL) return 0;
        LevelOrder(root);
        stack<TreeNode* > S;
        S.push(root);
        TreeNode* p,*pre=NULL;
        while(!S.empty())
        {
            p=S.top();
            if( (p->left==NULL && p->right==NULL) || (pre!=NULL &&(pre==p->left || pre==p->right)))
            {
                d(p);

                S.pop();
                pre=p;
            }
            else
            {
                if(p->right)
                    S.push(p->right);
                if(p->left)
                    S.push(p->left);
            }
        }
        map<TreeNode*, int> ::iterator it=nodemap.begin();
        int max=-1000;
        /*
        if(dp[it->second]==dp[nodemap[it->first->left]]+it->first->val)
            max=dp[it->second]+dp[nodemap[it->first->right]];
        if(dp[it->second]==dp[nodemap[it->first->right]]+it->first->val)
        {
            if(max<dp[it->second]+dp[nodemap[it->first->left]])
                max=dp[it->second]+dp[nodemap[it->first->left]];
        }*/

        for(;it!=nodemap.end();it++)
        {
            /*
            if(it->first->left!=NULL && dp[it->second]==dp[nodemap[it->first->left]]+it->first->val)
            {
                if(it->first->right!=NULL &&max<dp[it->second]+dp[nodemap[it->first->right]])
                    max=dp[it->second]+dp[nodemap[it->first->right]];
                else if(it->first->right==NULL)
                {
                    if(max<dp[it->second])
                        max=dp[it->second];
                }
            }
            if(it->first->right!=NULL && dp[it->second]==dp[nodemap[it->first->right]]+it->first->val)
            {
                if(it->first->left!=NULL &&max<dp[it->second]+dp[nodemap[it->first->left]])
                    max=dp[it->second]+dp[nodemap[it->first->left]];
                else if(it->first->left==NULL)
                {
                    if(max<dp[it->second])
                        max=dp[it->second];
                }
            }
            if(it->first->left==NULL && it->first->right==NULL)
            {
                if(max<dp[it->second])
                    max=dp[it->second];
            }
            else if(it->first->left!=NULL && it->first->right==NULL)
            {
                if(max<dp[it->second])
                    max=dp[it->second];
            }
            else if(it->first->left==NULL && it->first->right!=NULL)
            {
                if(max<dp[it->second])
                    max=dp[it->second];
            }*/

            if(it->first->left!=NULL && it->first->right!=NULL)
            {
                if(dp[it->second]==dp[nodemap[it->first->left]]+it->first->val && max<dp[it->second]+dp[nodemap[it->first->right]])
                    max=dp[it->second]+dp[nodemap[it->first->right]];
                else if(dp[it->second]==dp[nodemap[it->first->right]]+it->first->val && max<dp[it->second]+dp[nodemap[it->first->left]])
                    max=dp[it->second]+dp[nodemap[it->first->left]];
                else
                {
                    if(max<dp[it->second])
                        max=dp[it->second];
                }
            }
            else
            {
                if(max<dp[it->second])
                    max=dp[it->second];
            }
        }
        return max;
    }
    void LevelOrder(TreeNode* root)
    {
        queue<TreeNode* > Q;
        Q.push(root);
        int i=0;
        while(!Q.empty())
        {
            TreeNode* p=Q.front();
            nodemap[p]=i;
            i++;
            Q.pop();
            if(p->left!=NULL)
                Q.push(p->left);
            if(p->right!=NULL)
                Q.push(p->right);
        }

        memset(dp,0,sizeof(int)*i);
    }
    int d(TreeNode*p)
    {
        //if(!node[i])return dp[i]=0;
        //int l=node[i].l,r=node[i].r;
        int x;
        if((x=dp[nodemap[p]])!=0) return x;
        int lefti,righti,i=nodemap[p];

        if(p->left) 
        {    
            lefti=nodemap[p->left];
            dp[lefti]=d(p->left);
        }
        if(p->right)
        {
            righti=nodemap[p->right];
            dp[righti]=d(p->right);
        }

        //if(node[l])dp[l]=d(l);
        //if(node[r])dp[r]=d(r);

        if(p->left && p->right)
            return dp[i]=max(max(dp[lefti],dp[righti]),0)+p->val;
        else if(p->left && p->right==NULL)
            return dp[i]=max(dp[lefti],0)+p->val;
        else if(p->left==NULL && p->right)
            return dp[i]=max(dp[righti],0)+p->val;
        else 
            return dp[i]=p->val;
    }
    int Max(int a, int b, int c, int d)
    {
        int max=a;
        if(max<b) max=b;
        if(max<c) max=c;
        if(max<d) max=d;
        return max;
    }

    void Create(TreeNode* & root)
    {
        int data;
        cin>>data;
        if(data == -1) root=NULL;
        else
        {
            root=new TreeNode(data);
            Create(root->left);
            Create(root->right);
        }
    }

    void CreateLevelOrder(TreeNode* &root)
    {
        queue<TreeNode*> Q;
        int data;
        cin>>data;
        if(data==-1) 
        {
            root=NULL;
            return;
        }
        else 
            root=new TreeNode(data);
        Q.push(root);
        while(!Q.empty())
        {
            TreeNode* p=Q.front();
            Q.pop();
            cin>>data;
            if(data==-1)
                p->left=NULL;
            else
                p->left=new TreeNode(data),Q.push(p->left);
            cin>>data;
            if(data==-1)
                p->right=NULL;
            else
                p->right=new TreeNode(data),Q.push(p->right);
        }
    }

};

int _tmain(int argc, _TCHAR* argv[])
{
    freopen("in.txt","r",stdin);
    Solution S;

    S.Create(S.root);
    cout<<endl<<S.maxPathSum(S.root)<<endl;
    return 0;
}

后来还是改成了记忆化搜索,用瞿神的代码基础上,修改成leetcode的接口AC了

#include <unordered_map>
/**
 * Definition for binary tree
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */

class Solution
{


public:
    unordered_map<TreeNode*, int> nodemap;
    TreeNode *root;
    int ans=-0x3ffffff,cnt=0;
    Solution():root(NULL)
    {
    }
    int Create(TreeNode*& root)
    {
        int data,x;
        cin>>data;
        if(data == -1){
            root=NULL;
            return 0;
        }
        else
        {
            if(root!=NULL)
                root->val=data;
            else
                root=new TreeNode(data);
            //root->left=new TreeNode(0);
            //root->right=new TreeNode(0);
            Create(root->left);
            Create(root->right);
            //x=max(0,max(Create(root->left),Create(root->right)))+root->val;
            //nodemap[root]=x;
        }
    }
    int PreOrder(TreeNode* root)
    {

        if(root)
        {
            //cout<<root->val<<" ";
            if(nodemap.find(root)!=nodemap.end()) return nodemap[root]; 
            int x;
            x=max(0,max(PreOrder(root->left),PreOrder(root->right)))+root->val;
            nodemap[root]=x;
            return x;
        }
        else
            return 0;

    }
    int get_max(TreeNode *x)
    {
        if(!x)return 0;
        int tmp=nodemap[x];
        if(x->left&&x->right)
        {
            if(tmp==nodemap[x->left]+x->val&&nodemap[x->right]>0)return tmp+nodemap[x->right];
            if(tmp==nodemap[x->right]+x->val&&nodemap[x->left]>0)return tmp+nodemap[x->left];
        }
        return tmp;
    }
    int maxPathSum(TreeNode *x)
    {
        if(!cnt)PreOrder(x),cnt++;
        if(!x)return 0;
        if(x->left)ans=max(ans,maxPathSum(x->left));
        if(x->right)ans=max(ans,maxPathSum(x->right));
        ans=max(ans,get_max(x));
        return ans;
    }
};

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

UniquePath

这里两道题目,虽然只有一点差别,但是前者可以用一个组合数直接算出,后面则需要DP。

先看第一道,似乎是之前编程之美看过的Catalan数那题的一道课后习题,也是问从二维空间坐上到右下之类的,
最多有多少unique条路径,一开始我以为是n-1 里面插入m-1个位置,然后加上n-1个数两端n个插入m-1个位置,
然后以为是C(n,m-1),如果大的话反过来,后来发现错了,这样会漏掉m-1个数连续放在一起的情况,因为未必每个都单独占一个坑,
所以还是统一起来看,n-1+m-1个位置里选n-1(m-1), 不考虑顺序的,因为选定之后,只有一种排列,因此是组合数。但是测了发现OJ有个错误,
其实当n=m=100, 到最大范围时已经超了int的范围,如果用double每次乘以一个比例的话最后精度会丢导致结果出错,但是似乎最大198连乘到100似乎超过了
最大的范围了,包括unsigned long long, long double 都超了,所以即使不要求返回int,似乎需要bitset来处理乘积嘛。

class Solution {
public:
    int uniquePaths(int m, int n) {
        //double product=1;
        unsigned long long up=1,down=1;
        if(n>m) swap(n,m);
        for(int i=n-1;i>=1;i--)
        {
            up*=(unsigned long long)(m-1+i);
            down*=(unsigned long long)(i);
        }
        unsigned long long a=up/down;
        return (int)a;
        //product*=(double(m-1+i)/(i));//:i=n-1->1
        //return (int)product;
    }
};

第二道放了障碍,简单的DP,但是甩了几脚,当上和左都不是障碍时,我把条件与了,其实如果分别一个是的话,也是可以算的,所以这里没有仔细考虑算法,还有就是DP都是1…m 1..n这个是经验,
然后数据都从0开始,因此DP基本都要和数据下表相差1,数据要-1 包括ij

class Solution {
public:
    int dp[200][200];
    int uniquePathsWithObstacles(vector<vector<int> > &obstacleGrid) {

        int m=obstacleGrid.size();
        if(m==0) return 0;
        int n=obstacleGrid[0].size();
        if(n==0) return 0;
        for(int i=1;i<=m;i++)
        {
            for(int j=1;j<=n;j++)
            {
                dp[i][j]=0;
                if(obstacleGrid[i-1][j-1]==1) continue;
                if(i>=2 && j>=2)
                {
                    if(obstacleGrid[i-2][j-1]==0)
                        dp[i][j]+=dp[i-1][j];
                    if(obstacleGrid[i-1][j-2]==0)
                        dp[i][j]+=dp[i][j-1];
                }
                else if(i==1 && j>=2 && obstacleGrid[i-1][j-2]==0)
                    dp[i][j]=dp[i][j-1];
                else if(i>=2 && j==1 && obstacleGrid[i-2][j-1]==0)
                    dp[i][j]=dp[i-1][j];
                else if(i==1 && j==1)
                    dp[i][j]=1;
            }
        }
        /*
        for(int i=1;i<=m;i++)
        {
            for(int j=1;j<=n;j++)
                cout<<dp[i][j]<<" ";
            cout<<endl;
        }*/
        return dp[m][n];

    }
};

不优化反而变成了WA了,一直以为有什么边界没处理,结果是空间没优化,最后200MB内存,01背包还是要空间优化

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

Find Min in Rotated Array

剑指Offer一道题,一开始以为和leetcode一样,后来才发现leetcode是找target,Offer是找数组的最小元素。本质都是二分的应用,因为有序数组旋转其实是分成了两个有序数组,
有序数组的查找首选二分,但是至于怎么二分又是考察分析能力了。先从左部旋转一般情况考虑,即不考虑0个或n个的时候,因此旋转1…n-1个元素, 2 3 4 5 1及3 4 5 1 2的时候,中间数均比首尾数字要大,
其实如果大于首,说明mid一定在前一数组,前一个数组均大于后一数组元素,当然也大于尾,当4 5 1 2 3 及 5 1 2 3 4的时候,mid小于首,必位于后一数组,也必小于后一有序数组中后面的任一元素。

因此对于a[mid]>a[low], 搜索区间变为后面区间,low=mid+1, a[mid]<a[low], 搜索区间位于前面区间,因此且可能包含mid, 因为Mid位于后于区间,可能是min, high=mid, 接下来由于需要保持了每次a[low]是前一数组,
a[high] 是后一数组中,因此当最后搜索到low+1=high,时,high是后一数组的首元素,也即min,返回即可。因此前面不应该是low=mid+1,因此mid+1 可能属于后一区间了,要么特殊处理这个,所以前面改为low=mid可以
保证每次low在前一有序数组,high在后一有序数组的性质。

接下来考虑特殊情况,也即左旋0或n个元素,其实结果一样,此时第一次low<mid<high, 那么我就单独写一个条件语句,如果是这种直接返回a[low] 即可了。

因此代码如下: (当前不考虑出现重复数的情况)

int SearchMinIndexInRotatedSortedArray(int a[], int n) {
        int low=0,high=n-1;
        while(low+1<high)
        {
            int mid=low+(high-low)/2;
            if(a[mid]>a[low] && a[mid]>a[high])
                low=mid+1;
            else if(a[mid]<a[low] && a[mid]<a[high] )
                high=mid;
            else if(a[mid]<a[high] && a[mid]>a[low])
                return low;
        }
        return high;
    }

最后吸取荷兰国旗的教训,没测过OJ的都不敢保证正确性,基于上述找minidnex算法,写了一个leetcode在左旋数组里找target元素的算法,因为模仿上面从中间来直接和target元素比大小似乎看不出
target运算应该在上区间和下区间,因此还是先找minindex,然后和a[0]比较大小以确定在上区间还是下区间,然后进行二分就可以了,但是还要处理这种没有旋转的特殊情况,尤其注意
这种找min的算法while循环执行至少需要有3个element,因此要处理下0 1 2的情况是否可以统一起来。这道题目没有用很大的数据恰时间,因此随便一个线性查找都可以解决,不过面试可是有面试官的,因此
还是要拿对数时间的算法。

class Solution {
public:
    int search(int a[], int n, int target) {
        if(n==0) return -1;
        int low=0,high=n-1;
        bool rotated=true;
        while(low+1<high)//at least three ele
        {
            int mid=low+(high-low)/2;
            if(a[mid]>a[low] && a[mid]>a[high])
                low=mid;
            else if(a[mid]<a[low] && a[mid]<a[high] )
                high=mid;
            else if(a[mid]<a[high] && a[mid]>a[low])
            {
                rotated=false;
                break;
            }
        }
        //return a[high];
        if(n==2)//two ele
        {
            if(a[0]<a[1])
                rotated=false;
            else
                ;
        }
        int l,h;
        if(rotated==false)
            l=0,h=n-1;
        else
        {
            if(target>=a[0])//former interval
                l=0,h=low;
            else if(target<a[0])//latter interval
                l=high,h=n-1;
        }

        while(l<=h)
        {
            int mid=l+(h-l)/2;
            if(a[mid]<target)
                l=mid+1;
            else if(a[mid]>target)
                h=mid-1;
            else
                return mid;
        }
        return -1;

    }
};

今天群里说到指针排序,差点忘记了,之前好像是学过的,可以避免频繁的数据交换

顺便补充下,今天突然有点忘了的位运算,找x的最低位为1,其他bit赋0的数,用x&(-x) 可以得到,或者x& ~(x-1), 因为x>0的时候,-x 取反加1了,这个是找只有两个数字只
出现一次,而其他数字都出现偶数次,那么返回这两个数中任意一个的时候用到,firstonebit=x&(-x) 接下来就可以每个数a[0..n-1]&firstonebit便可以将剩余的数分成两组了,
为什么是找最低位为1的呢,主要是因为得到这个数方便,完全可以找任意一位bit为1的,这个数也是有32bit的一个数,只是x的最低位为1的位为1,其他都为0,这样一个数。

for(int i=0;i<len;i++)
    std::count<<*result++;

看到July博客的代码,这个不由得想起运算符优先级,MS校招笔试还考过,post-increment>*(取内容)>pre-increment,因此是先后面但是后增是先参与一次运算,再自增,又是相当于
先去当前result指向的值,再往后移。

http://www.cppblog.com/aqazero/archive/2012/11/10/8284.html

绝对值的或运算又有点忘记了

int i=a>>31;
return (a^i)-i

今天测了一道KMP居然TLE了,于是乎以为是需要优化,想起July的优化next数组,但是似乎没有对时间进行优化,中间WA TLE WA了,然后AC,期间范的错误现在总结起来依次为 忘记删除文件重定向了,TLE是
不能用iostream做输入输出,和scanf prinf在大数据上性能确实可能存在很大的差距,本次实测也是如此,差点以为算法还有问题。。。后一次WA是以为转化为char数组可以提高性能,后来发现本地编造的数据
都是0-9所以可以转char完全不考虑题目的数据范围,以为int一次加4个字节会比较慢,其实是错误的,然后终于AC了,还是MSVC编译的,而且他有next的函数,以至于产生重名要改next名字。

另外认识群里华科和东北大学的ACMer,还有输入输出加速,不过了解下最贵没坏处,都是利用getchar 和putchar的高效率来实现的,利用递归思想
void out(x)
{
if(x>9)
out(x/10);
putchar(x%10+’0’);
}

输入则是处理正负数实现 http://blog.csdn.net/shahdza/article/details/6317011

今天发现GCC 3.3的版本编译iostream 下的 scanf printf 可以, 到了4.7就不行了,所以C的还是尽量stdio.h吧

今天想个题目,找和为偶数的最长子序列,首选最长字段和,突然发现自己有一种新的理解这个递推方程的思路,比之前的好理解多了,非常有说服力,b[j]还是表示以a[j]为子序列尾的最大字段和的最优值,
那么b[j]长度要么为1,要么>=2吧,按长度来分,如果长为1,就是a[j],如果>=2就是至少包含a[j] a[j-1], 那么里面一定蕴含了一个b[j-1]的最优解,如果b[j-1]最优的话,b[j]也最优,因此b[j]=max{b[j-1]+a[j], a[j]}
然后才考虑b[j-1]<0 选后者,大于0选前者,而不是本末倒置。但是发现偶数的这个题,不具备这个性质似乎,因为b[j-1]左边再加奇数,a[j]又是奇数,就会至少b[j-1]+2了,还与b[j-1]左端有关了。

想Kruskal算法的时候,有集合的操作,主要是查找元素所在的集合,以及集合的合并操作,想起了并查集,STL有个set但是里面不提供集合合并的操作,于是查到set_union, 还有merge这两个都是合并两个有序数组的算法,只是
前者删除重复数,后者保留,因此也称为何叫set union了,复杂度O(m+n) 但是我不需要有序,似乎会浪费些时间,于是看到set1.insert(set2.begin(),set2.end())的做法,但是这种不好,因为到时候怎么去维护这些集合呢,
频繁的删除操作,因此需要list存放sets么,好像太复杂了,发现还是裸写一个并查集比较好,这个是专门处理不想交集合的,维护方便,代码也不难。什么时候再连一个,就当练习并查集,这个还是比较重要的。

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

Revocer BST

这道题目看起来思路很好想,BST中序就是排序,等价于找排序数组调换的两个元素,一开始分析拿几个例子,大概有两种要注意,一种是相邻swith,一种是涉及到头尾的switch,大致组合四种
case,但是后来发现其实就是相邻switch这一种情况,相邻只有一次decrease,否则有两次,头尾可以归纳到相邻,而且自己犯了一次错误,以为相邻只要记住第一个,然后后面next访问就可以了,
后来发现其实是BST,没next= =通过InOrder遍历找,于是也要记录miss2node, 第二次reverse刚好可以覆盖成新的,正好正确。

代码:

void recoverTree(TreeNode *root) {
        stack<TreeNode*> s;
        TreeNode*p=root, *mis1node, *mis2node, *lastp=NULL;

        int reversecount=0;
        while(!s.empty()||p!=NULL)
        {
            while(p!=NULL)
            {
                if(lastp!=NULL && lastp->val>p->val)
                {
                    reversecount++;
                    if(reversecount==1)
                    {
                        mis1node=lastp;
                        mis2node=p;
                    }
                    else if(reversecount==2)
                    {
                        mis2node=p;
                    }
                }

                lastp=p;
                s.push(p);
                p=p->left;
            }
            p=s.top();
            s.pop();
            p=p->right;
        }

        if(reversecount==2|| reversecount==1)
        {
            swap(mis1node->val,mis2node->val);
        }
    }

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

Remove Duplicates

这是一道简单程设,但是写起来还是不利索,写链表版更麻烦了,本来这种中间插入删除是利于用链表的,但是先试试数组版的,
至少这些都需要,思路比较简单,找count>2 的多出来的区间,删掉就可以了,思考的过程如下:

  1. 找多出2的重复数,while里面有a[i]=a[i+1],一开始写的a[i] a[i-1] 但是后面找多出重复数区间又是a[]i a[i+1],然后意识到
    统一比较好,于是前面也改成a[i] a[i+1]

2.有i+1 i++ 想到越界,前面加上i<=n-1, 后面才想到长度在变,要用length,或者直接n 来变也行

  1. count<2 初始count=1, 一开始以为count=3 可以少一种while exit条件的讨论,后来发现不行

4.while(i<=length-2 && a[i]==a[i+1] && count<2) 退出循环有8中情况,我们来一层层博洋葱,如果越界了,一定是count<="2" 因为如果count前面="2" 就退出循环不会到这一轮了,越界了自然没有a[i+1]="" 也没有a[i]="" a[i+1]关系的概念了。count<="2" 没有删除元素直接返回或者break循环即可,一次剥掉4种情况。="" 后面i<="length-2," a[i]!="a[i+1]" 同上分析必count<="2," 那么没有需要删除的元素,直接continue下一轮while,注意i要后移,同时我submit漏了count="1,因为每一次while开始都必须保证" count清为1,至少重复一次。接下来就是正常的了,count<="2," 且a[i]="=a[i+1]" 说明重复元素="">=3, 然后后面注意控制,一个while找重复区间,注意同样不越界,类似前述分析,然后最后一定记得length或n要减去[start, end] 区间长度,end-start+1, 最后i=start,
表示后移元素的第一个的新的位置

其实这样一个简单的程设里面包含的条件分析也很多的,最开始学的时候,这个肯定会出很多错误,关键是while退出条件的分析,
这就像一套规则要适用所有数据,其实是不简单的。

class Solution {
public:
    int removeDuplicates(int a[], int n) {
        int count=1;
        int i=0,start,end,length=n;
        while(i<length)
        {
            while(i<=length-2 && a[i]==a[i+1] && count<2)
                count++,i++;
            if(i>=length-1)
            {
                break;
            }
            if(a[i]!=a[i+1])
            {
                i++;
                count=1;
                continue;
            }
            i++;
            start=i;
            while(i<=length-2 && a[i]==a[i+1])
                i++;
            end=i;
            //delete(start,end);
            count=1;
            for(int l=start,m=end+1;m<=length-1;l++,m++)
                a[l]=a[m];
            length-=(end-start+1);
            //a[start+1]=a[end+2];
            //...
            //=a[n-1];

            i=start;
        }
        return length;
    }
};

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

Subsets-BagProblem

今天问了Adhoc大神,才知道子集和还有DP算法,难怪之前一直过不了那道去重复解,排序的子集和问题,算法就是错的,DFS超时,于是搜到了背包一系列,这个不论是ACM,还是面试都是考察的重点,
DP的精髓,于是看到了著名的背包九讲,决定慢慢吭,另外还搜到了diaorui大牛的博客,今年刚去Google,做最优化,感觉背包
还限制在01背包和贪心的可以选部分比的背包。。。现在才知道有很多背包,而且最关键的是子集和问题居然还有DP算法。。。

于是乎打算再次回顾DP,主要在背包,另外还有搜索空间的一些变化总结下,主要是N行和N+1行的区别

先赋上这道题目代码:

bool dp[10000][10000];
class Solution
{
    public:

        vector<int> onesolution;
        vector<vector<int> > allsolution;
        vector<vector<int> > combinationSum2(vector<int> &num, int target)
        {
            allsolution.clear();
            onesolution.clear();
            sort(num.begin(),num.end());
            /*
            for(int i=0;i<=num.size()-1;i++)
            {
                for(int j=0;j<=target;j++)
                    dp[i][j]=false;
            }*/
            KnackBag(num,target);
            dfs2(num.size(),target,num);
            vector<vector<int> > uniquesolution;

            //if(allsolution.size()<=0)
            //    return uniquesolution;
            //sort(allsolution[0].begin(),allsolution[0].end());
            //uniquesolution.push_back(allsolution[0]);
            //sort(uniquesolution[0].begin(),uniquesolution[0].end());
            for(int i=0;i<=(int)allsolution.size()-1;i++)
            {
                /*
                if(allsolution[i]!=allsolution[i-1])
                {
                    //sort(allsolution[i].begin(),allsolution[i].end());
                    uniquesolution.push_back(allsolution[i]);
                    sort(uniquesolution.back().begin(),uniquesolution.back().end());
                }*/
                sort(allsolution[i].begin(),allsolution[i].end());
                bool isunique=true;
                for(int j=0;j<(int)uniquesolution.size();j++)
                {
                    if(allsolution[i]==uniquesolution[j])
                    {
                        isunique=false;
                        break;
                    }
                }
                if(isunique)
                    uniquesolution.push_back(allsolution[i]);
            }
            return uniquesolution;
        }

        void KnackBag(vector<int>& num, int target)
        {
            int N=num.size();
            for(int i=0;i<=N;i++)
            {
                dp[i][0]=true;
            }
            for(int j=1;j<=target;j++)
                dp[0][j]=false;
            for(int i=1;i<=N;i++)
            {
                for(int j=1;j<num[i-1];j++)
                    dp[i][j]=dp[i-1][j];
                for(int j=num[i-1];j<=target;j++)
                    dp[i][j]=dp[i-1][j] || dp[i-1][j-num[i-1]];
            }
        }


        void dfs(int i, int j,vector<int>& num)
        {
            if(i>0)
            {
                if(i>=1 && j>=0 && dp[i][j]==false)
                    return;
                else
                {
                    dfs(i-1,j,num);
                    if(j>=num[i-1])
                    {
                        onesolution.push_back(num[i-1]);
                        dfs(i-1,j-num[i-1],num);
                        onesolution.pop_back();
                    }
                }
            }
            else if(i==0)
                allsolution.push_back(onesolution);
        }

        void dfs2(int i,int j,vector<int>& num)
        {
            if(i>=0 && j>=0 && dp[i][j]==false) return;
            else
            {
                if(i==0)
                    allsolution.push_back(onesolution);
                else
                {
                    dfs2(i-1,j,num);
                    if(j>=num[i-1])
                    {
                        onesolution.push_back(num[i-1]);
                        dfs2(i-1,j-num[i-1],num);
                        onesolution.pop_back();
                    }
                }
            }
        }
};

int main()
{
    //write;
    Solution S;
    int a[]={13,23,25,11,7,26,14,11,27,27,26,12,8,20,22,34,27,17,5,26,31,11,16,27,13,20,29,18,7,14,13,15,25,25,21,27,16,22,33,8,15,25,16,18,10,25,9,24,7,32,15,26,30,19};
    vector<int> num(&a[0],&a[sizeof(a)/sizeof(int)]);
    int target=25;
    time_t start=clock();
    vector<vector<int> > solution=S.combinationSum2(num,target);
    time_t end=clock();
    cout<<end-start<<endl;
    for(int i=0;i<solution.size();i++)
    {
        for(int j=0;j<solution[i].size();j++)
            cout<<solution[i][j]<<" ";
        cout<<endl;
    }
    return 0;
}

里面主要是又犯了DP的递推公式的问题,也即DP搜索空间本来应该N+1行又写成了N行了,还有回溯求解这块有点生疏了,记得当时矩阵链,LCS等回溯的时候好像也不是很顺。

于是又重新看了一下DP算法的几个问题,其实应该都有一个i=0的 不存在的一个解空间行,这样每次回溯到这里就是构造一个解的过程结束,并且尤其要注意这一行的初值的情况

例如LCS MinEditDis 01Bag等等,其实都是一类,从i=0->N, 开始构造,和矩阵链不同,矩阵链是中间断开作为一次选择,因此dp[i][j]长度应该从短到长递推求解状态图状态,矩阵链的初始状态,或者说
递归出口是dp[i][j] i=j的情况,不需要额外找一个i=0的空行。

回到01背包,dp[i][j], i=0->N, j=0->target, 那么初始值为i=0, 也即一个物品都不选,背包>=0 最优值都为0, 因为没有物品可选

所以递推伪代码:
for i=1->N
for j=0->a[i-1]-1
dp[i][j]=dp[i-1][j]
for j=a[i-1]->target
dp[i][j]=max(dp[i-1][j],dp[i-1][j-a[i-1]+v[i-1]];

把他运用到子集和问题,需要修改下状态图的定义,原始的是dp[i][j]表示 选前i个背包,包重j时,最大价值(包未必装满,如果要求装满只需要改个初值条件,源自背包九讲),
子集和问题,v[i]=w[i], 不再侧重于最后的value,因为最优value都是 target,而是把所有解求出,也即一个01向量,所以dp[i][j]这里表示 用前i个背包,和j的子集是否存在,bool的状态空间,
初值条件特别当心,dp[0][j]=0, j=1..target, 表示j不空,不选包不存在解,这是显然的,dp[0][0]=1,这个要特别注意,不选物品,包重0, 存在一个空集解,子集和解也可以是空集,只是题目说了和都是正整数罢了,
所以这也是自己之前一再强调的对于初始条件,一定要分析清楚,不要盲目赋0,

因此递推伪代码(基于完全背包的子集和问题):

for i=1->N
    for j=0->target
        dp[i][j]=false;
        for k=0;k*a[i-1]<=j;k++
            dp[i][j]+=dp[i-1][j-k*num[i-1]]

这里面受到了adhoc的启发,本来是或运算,现在改成等价于bool值的加法,其实bool只有二值,多个或只要一个true就是true,和加法一样的。但是一定要记得dp[i][j]初始为false,
要么一开始memset,要么每次两重循环一进来就赋false

有了这个之后就是回溯求解这块要特别注意,如果dp[i][j]=false, 那么就当前无解了,直接返回,否则看是否递归到底,即遍历完所有的数了,其实说白了01背包是多重背包的特列,只是一开始学的时候
写成单独的两项,其实也可以写成完全背包的式子,k=0 1两种情况

void dfs2(int i,int j,vector<int>& num)
            {
                if(i>=0 && j>=0 && dp[i][j]==false) return;
                else
                {
                    if(i==0)
                        allsolution.push_back(onesolution);
                    else
                    {
                        dfs2(i-1,j,num);
                        if(j>=num[i-1])
                        {
                            onesolution.push_back(num[i-1]);
                            dfs2(i-1,j-num[i-1],num);
                            onesolution.pop_back();
                        }
                    }
                }
            }

赋上diaorui大牛的博客 http://diaorui.net/archives/567

以及著名的OIer Cui牛的 背包九讲 http://diaorui.net/archives/567

北邮某同学帖子,比较认同为啥SSP问题不讲DP解法的观点 http://bbs.byr.cn/#!article/ACM_ICPC/43221

这里再对DP状态空间做个总结,像这种每次做的选择划分成n-1规模的一个子问题,一般都要构建一个不存在的N=0的解空间行,构造解回溯到这里表示结束,也是初始递推状态。这也是受cuitianyi大牛的影响,
虽然他没有将背包解法推广到其他问题,这个推广是看到北邮一个同学说的,我也觉得纳闷这种DP解法教材都不讲,后来问沈老师,他好像也不是很了解。

Adhoc大神确实太强,用无与伦比来形容他的DS Alg能力毫不为过,只可惜没有代表复旦ACM比赛,打Final问题不大。这也无可厚非,大学才开始接触编程,算法,数据结构的人怎么和 从小学就开始
编程,Alg DS的人比呢,就和音乐人一样,怎么和4岁开始学钢琴的人比,周董的钢琴已经不能再熟了,所以小孩从小的培养很重要,尤其是明确知道自己喜欢什么,以后走那条路的时候。

或运算本质就是bool值加法,异或本质似乎是bool值带进位,且进位丢弃的加法。

01背包 时复 O(NC) 著名的伪多项式算法,空复O(NC), int类型状态空间,cui牛博客说可以空复降到O(C),求最优值,一次循环构造唯一解
01选法子集和 时复 同上,空复O(NC), bool 类型状态空间,无最优值,有多个解集,递归类dfs算法构造解集,参数加上dp[][], dp[][]=false return

完全背包 时复O(NC*max(C/a[i])), 复杂度较高,空复O(NC), int类型状态空间
完全选法子集和,时复同上,空复O(NC), bool 类型状态空间,无最优值,有多个解集,递归类dfs算法构造解集,参数加上dp[][], dp[][]=false return

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

TrapRainWater

这道题目看起来挺复杂的,满足装水的形状就是一个平的子序列,左右都比他大,就可以装水了,但是补了之后,可能还要再补,这是比较麻烦的,尤其是逻辑设计比较纠结。

符合情况的1<=i<=n-2, 因为端点肯定是不能装水的,只有一端拦着。

因此,我先是想了一个多遍扫描,每遍填一层,然后通过一个bool值判断,如果该层没有填就结束了。思路就是首先找符合(a[i-1]>a[i] && a[i]<=a[i+1]),只有这种i才有可能是要填水的起点,
否则直接i++,这里面我现在牢记Knuth说的过早优化是万恶的源头,把else也写出来这样逻辑清晰一点,即使是空语句,尤其是后一种算法不写else根本看不清楚。然后里面再去向后延伸找
最长的一层平的,然后开始填,然后后面对于每一层都这么处理,直到有一次扫描找不到填坑就结束了。

int trap_morepass(int a[], int n)
{
    int count=0,i,start;
    bool trap;
    while(1)
    {
        i=1;
        trap=false;
        while(i<=n-2)
        {
            if(a[i-1]>a[i] && a[i]<=a[i+1])
            {
                start=i;
                while(i<=n-2 && a[i]==a[i+1])
                    i++;
                if(a[i]<a[i+1])
                {
                    count+=(i-start+1)*(min(a[start-1],a[i+1])-a[start]);
                    trap=true;
                    for(int j=start;j<=i;j++)
                        a[j]=min(a[start-1],a[i+1]);
                }
                else
                    ;
            }
            else
                ;
            i++;
        }
        if(trap==false)
            break;
    }
    return count;
}

后来发现TLE了,于是打算优化,上述确实可能很慢,极端情况扫描次数可能是里面max(a[i]),于是优化着眼点还是把里面的多遍扫描变成一遍,也就是填了水之后,就地在这基础上再看是否
需要填水,因此if改成while,如果填了水之后,左端可能会延伸,如果延伸后降了,就不能再填水了,否则需要处理,这里while将i跳到j,也即新的可能的
填水的起点,代码依然不优化,把else留着,这样逻辑结构非常清晰。

int trap_onepass(int a[], int n)
{
    int count=0,i,start,j;
    bool trap;
    //while(1)
    //{
        i=1;
    //    trap=false;
        while(i<=n-2)
        {
            while(a[i-1]>a[i] && a[i]<=a[i+1])
            {
                start=i;
                while(i<=n-2 && a[i]==a[i+1])
                    i++;
                if(a[i]<a[i+1])
                {
                    count+=(i-start+1)*(min(a[start-1],a[i+1])-a[start]);
                    //trap=true;
                    for(j=start;j<=i;j++)
                        a[j]=min(a[start-1],a[i+1]);

                    j=start;
                    while(j>=1 && a[j-1]==a[j])
                        j--;
                    if(j>=1)
                    {
                        if(a[j-1]>a[j])
                            i=j;
                        else
                            break;
                    }
                    else
                        ;
                }
                else
                    ;
            }
            //else
            //    ;
            i++;
        }
    //    if(trap==false)
    //        break;
    //}
    return count;
}

本地测试最大的数据10732大小数组, 时间从2s降到1s,但可惜的是还是TLE了,暂时放一放。。。。

坑爹,发现里面count+ 和a[j]需要重新附上填坑后的值,于是发现之前的语句错了,要找两端较小的,所以Leetcode很多TLE其实是WA,但是即使这样有一个简单的case过不了,[4,2,3], 明明本地是输出1的,OJ输出了3,不知道
为啥。。。

后来搞了半天,代码漏掉一个越界处理,于是乎发现了VC和GCC的区别了,两者对于数组越界都不报错,这是访问了一个野值而已,而VC野值是一个很大的正数,GCC是负数,因此到了if(a[i]<a[i+1])时,
VCtrue,GCCfalse了,主要是上面while有两个条件与,因此每个条件结束循环都要处理。。附上AC代码。。。

class Solution {
public:
int trap(int a[], int n)
{
    int count=0,i,start,j;
    bool trap;
    //while(1)
    //{
        i=1;
    //  trap=false;
        while(i<=n-2)
        {
            while(a[i-1]>a[i] && a[i]<=a[i+1])
            {
                start=i;
                while(i<=n-2 && a[i]==a[i+1])
                    i++;
                //if(i>n-2) break;
                if(a[i]<a[i+1])
                {
                    count+=(i-start+1)*(min(a[start-1],a[i+1])-a[start]);
                    //trap=true;
                    for(j=start;j<=i;j++)
                        a[j]=min(a[start-1],a[i+1]);

                    j=start;
                    while(j>=1 && a[j-1]==a[j])
                        j--;
                    if(j>=1)
                    {
                        if(a[j-1]>a[j])
                            i=j;
                        else
                            break;
                    }
                    else
                        ;
                }
                else
                    ;
            }
            //else
            //  ;
            i++;
        }
    //  if(trap==false)
    //      break;
    //}
    return count;
}
};

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

ReplaceSpace to Strcpy fault

一道剑指Offer的简单题,越简单越容易忽视错误。很多带有非ASCII码的字符在互联网上传播的时候都会转化为ASCII嘛字符,
最常见的就是wikipedia有些中文的名词,那么把URL copy发给别人的时候都会转化为ASCII码,例如space->%20这类的。

这种字符串替换一类的特别注意空间会往后扩展不要覆盖原来的字符。思路主要有两个:

  1. O(n), two-pass 就地操作,先计算出空格数,增加的长度是空格数2, 然后由于字符都向后移,因此避免覆盖还未处理的字符,从后向前遍历,每个
    字符都向后移前面的空格数
    2个位置

2.O(n) one-pass, O(n) space 申请一个堆,one pass, 两个指针,算法比较清晰,就是要另外占空间,而且最后还要记得free掉这个空间

3.O(n^2) 从前向后,每次遇到空格后面就整体后移当前遇到空格数*2个位置

这里面有一个地方自己要特别当心,就是当copy %20这三个字符时,可能觉得一个字符copy麻烦,直接strcpy却殊不知strcpy会
多做一件事情就是尾部加\0, 所以要特别当心,用newheap的方法,由于后面会修改\0的位置,所以strcpy没问题,但是最好还是把这件事情前面
就做好,不要漏电什么,也就是用strncpy来copy,指定几个字符,这个是纯粹的copy字符,不拖泥带水的。

char* ReplaceSpace_newheap(char* cstr)
{
    int cstrlen=strlen(cstr);
    char* resultstr=new char[cstrlen*3+1];
    int i=0,j=0;
    while(i<cstrlen)
    {
        if(cstr[i]!=' ')
        {
            resultstr[j]=cstr[i];
            i++;j++;
        }
        else
        {
            strncpy(resultstr+j,"%20",3);
            j+=3;
            i++;
        }
    }
    resultstr[j]='\0';
    //strcpy(cstr,resultstr);
    //delete []resultstr;
    return resultstr;
}

char* ReplaceSpace_inplace_countspace(char* cstr)
{
    int i,totalcount=0;
    for(i=0;cstr[i];i++)
    {
        if(cstr[i]==' ')
            totalcount++;
    }
    int currentcount=0;
    int cstrlen=i;


    cstr[cstrlen+(totalcount-currentcount)*2]='\0';
    for(int j=cstrlen-1;j>=0;j--)
    {
        if(cstr[j]!=' ')
            cstr[j+(totalcount-currentcount)*2]=cstr[j];
        else
        {
            currentcount++;
            strncpy(cstr+j+((totalcount-currentcount)*2),"%20",3);
        }
    }
    return cstr;
}

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