1.算法入门_贪心
1.1 连续子数组的最大和
不用贪心的话,判断有问题。如果先设max
值为0,那么可能无法输出负数值,如果++j
判断,那么最后一个元素没办法进入判断。
class Solution {
public:
int FindGreatestSumOfSubArray(vector<int> array) {
vector<int> src(array);
int max=src[0],maxlen;
for(int i=0;i<src.size();i++)
{
int sum=src[i],len=1;
//max=sum;
int j=i;
while(++j<src.size())
{
sum+=src[j];
len++;
if(sum>max)
{
max=sum;
maxlen=len;
}
}
}
return max;
}
};
采用数组保存每个索引开始后子数组最大值,但是会超时。
class Solution {
public:
int FindGreatestSumOfSubArray(vector<int> array) {
vector<int> src(array);
//vector<int> src{-1,-2,-3,-4,5};
vector<int> inmax; //不要给初值0
for(int i=0;i<src.size();i++)
{
int sum=src[i];
int max=sum;
int j=i+1;
while(j<src.size())
{
sum+=src[j];
if(sum>max) max=sum;
j++;
}
inmax.push_back(max);
}
sort(inmax.begin(),inmax.end());
//cout << inmax[inmax.size()-1];
return inmax[inmax.size()-1];
}
};
仍旧是思路转变:不要总是从前往后找,从前面找不能确定什么时候开始什么时候结束,但是从后面找一定能确定结束位置。
解法:每一位找最大的子数组之和,关键在于正数相加,有负数加上也没关系,只要max
不变,那么比的还是和的最大值。在统计的时候只要判断正负就可以用负数分隔出子数组来。
class Solution {
public:
int FindGreatestSumOfSubArray(vector<int> array) {
if(array.size()==1) return array[0];
int max=array[0];
for(int i=1;i<array.size();i++)
{
if(array[i-1]>0) array[i]+=array[i-1];//子数组求的是正数结果和
if(max<array[i]) max=array[i];
}
return max;
}
};
参考:
动态规划思路:求解子问题,确定当前问题和前一个子问题的关系。(两者依据情况可交换顺序)
1.2 买卖股票的最好时机(一)
这道题的思路和上面是一脉相承的。
主要思路是判断盈亏,那么先麻烦一下,求相隔天之间的盈亏值,然后用上一题的求最大子数组和思路,求最大盈亏值。
class Solution {
public:
/**
*
* @param prices int整型vector
* @return int整型
*/
int maxProfit(vector<int>& prices) {
// write code here
if(prices.size()==1) return 0;
vector<int> diff(prices.size()-1);//肯定至少有2天,不然没法买卖
for(int i=1;i<prices.size();i++)
{
diff.push_back(prices[i]-prices[i-1]);//获取间隔天的盈亏值,然后就可以用判盈亏累加的方式求最大值
}
int win=diff[0];
for(int j=1;j<diff.size();j++)
{
if(diff[j-1]>0) diff[j]+=diff[j-1];//如果前一天盈利就加上
if(win<diff[j]) win=diff[j];
}
if(win<0) return 0;
else return win;
}
};
大佬的简约思路:确定当前值和之前最小值之差。
那么需要确定2个值:当前最小值,当前盈利最大值。
class Solution {
public:
/**
*
* @param prices int整型vector
* @return int整型
*/
int maxProfit(vector<int>& prices) {
// write code here
int ans=0,mi=prices[0];
for(int i=1;i<prices.size();i++)
{
ans=max(ans,prices[i]-mi);
mi=min(mi,prices[i]);
}
return ans;
}
};
1.3 买卖股票的最好时机(二)
仍旧是上一题的套路,先求相隔天的盈亏值,发现只要有盈利加起来就行。
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
* 计算最大收益
* @param prices int整型vector 股票每一天的价格
* @return int整型
*/
int maxProfit(vector<int>& prices) {
// write code here
vector<int> diff(prices.size()-1);
for(int i=1;i<prices.size();i++)
{
diff.push_back(prices[i]-prices[i-1]);//先求相隔天的盈亏值
}
int sum=0;
for(int j=0;j<diff.size();j++)
{
if(diff[j]>0) sum+=diff[j];
}
return sum;
}
};
1.4 通配符匹配
简单思路就是逐位判断,但是这种方法是有弊端的,只能判断非*
开头的情况,大概通过31/45左右。
class Solution {
public:
bool isMatch(const char *s, const char *p) {
if(*s=='\0' && !(*p=='*' || *p=='\0')) return false;
stack<char> tmp;
while(*s!='\0')
{
tmp.push(*s++);
if(*p==tmp.top() || *p=='?') { tmp.pop(); p++; }
}
if(*p=='*' || tmp.empty()) return true;
else return false;
}
};
果然还是要用动态规划。需要理清楚所有情况。
参考通配符匹配。
用dp[i][j]表示s中前i个字符和p中前j个字符的匹配关系。如果用s匹配p不好匹配,因为p中字符可能空匹配或者多匹配,所以要用p去匹配s。
有如下3大种情况:
1)p[j]为小写字母,则要保证s[i]和p[j]匹配,则s第i位和p第j位匹配,且s前i-1位要和p前j-1位匹配,状态转移方程:dp[i][j]=dp[i-1][j-1]
2)p[j]='?',同样要保证s[i]和p[j]匹配,s第i位和p第j位匹配,且s前i-1位要和p前j-1位匹配,状态转移方程:dp[i][j]=dp[i-1][j-1]
3)p[j]='*',有2类情况:
a)'*'对应空字符,则p第j位对应s空位,要保证p前j-1位和s前i位对应,状态转移方程:dp[i][j]=dp[i][j-1]
b)'*'对应多个字符,则p第j位对应s第i位或者包含i的多位,要保证p前j位和s前i-1位对应,状态转移方程:dp[i][j]=dp[i-1][j]
此外,考虑边界情况。
1)dp[0][0]表示s和j均为空,此时dp[0][0]=1
2)p为空s不为空,必不匹配,此时dp[i][0]=0
3)s为空p不为空,只有p为'*'或者多位'*'才匹配,此时dp[0][j]=1
java
程序比较好读一点:
class Solution {
public:
bool isMatch(const char *s, const char *p) {
int dp[1005][1005];
dp[0][0]=1;
for(int i=1;i<=p.size();i++)
{
if(p[i-1]=='*') dp[0][i]=true;
else break;
}
for(int i=1;i<=s.size();i++)
{
for(int j=1;j<=p.size();j++)
{
if(p[j-1]=='*') dp[i][j]=dp[i][j-1] | dp[i-1][j];
else if(p[j-1]=='?' || p[j-1]==s[i-1]) dp[i][j]=dp[i-1][j-1];
}
}
return dp[s.size()][p.size()];
}
};
C++
程序:
注意一下,如果用字符数组,那一定要用str
开头的那些函数去操作。
class Solution {
public:
bool isMatch(const char *s, const char *p) {
int m=strlen(s),n=strlen(p);
vector<vector<int>> dp(m+1,vector<int>(n+1,false));//注意要定义多长度1位,用于给空字符串
dp[0][0]=true;
for(int i=1;i<=n;i++)
{
if(p[i-1]=='*') dp[0][i]=true;
else break;//这步很巧妙,一旦p中有非*的,那肯定不匹配,直接跳出循环
}
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
{
if(p[j-1]=='*') dp[i][j]=dp[i][j-1] || dp[i-1][j];
else if(p[j-1]=='?' || p[j-1]==s[i-1]) dp[i][j]=dp[i-1][j-1];
}
}
return dp[m][n];
}
};
1.5 分糖果问题
如果涉及到自己定义排序函数,那么需要注意几点,参考C++ 二维vector排序(sort用法)。
一般是自定义一个cmp
函数,则注意:
1)形参是指针,2)函数加static
关键字,因为sort
的调用必须是静态成员
解题思路:
先把所有得分从低到高排列,然后判断每个索引前后有没有糖果,没的话,直接赋1;有的话,如果得分和前(后)相同,就取前一个糖果数;不相同,就取前一个糖果+1。
测试的时候只有6/15,超出就不行了。
# error
class Solution {
public:
/**
* pick candy
* @param arr int整型vector the array
* @return int整型
*/
static bool cmp1(const vector<int> &a,const vector<int> &b)//注意这里形参是指针,加static关键字,sort调用必须是静态成员
{
return a[1]<b[1];
}
int candy(vector<int>& arr) {
// write code here
int m=arr.size();
vector<vector<int>> dp(m,vector<int>(2,0));//第1列索引,第2列得分
vector<int> can(m+2,0);//糖果,比索引多2位,和索引相比大1
for(int i=0;i<m;i++)
{
dp[i][0]=i;
dp[i][1]=arr[i];
}
sort(dp.begin(),dp.end(),cmp1);//按照第2列得分升序排列
int candy=0;//统计糖果总数
can[0]=0,can[m+1]=0;
for(int i=0;i<m;i++)
{
if(can[dp[i][0]]==0 && can[dp[i][0]+2]==0)
{
can[dp[i][0]+1]=1;
candy+=can[dp[i][0]+1];
}
else if(arr[dp[i][0]]==arr[dp[i][0]-1])
{
can[dp[i][0]+1]=can[dp[i][0]];
candy+=can[dp[i][0]+1];
}
else
{
can[dp[i][0]+1]=max(can[dp[i][0]],can[dp[i][0]+2])+1;
candy+=can[dp[i][0]+1];
}
}
return candy;
}
};
参考:题解 | #分糖果问题#
大佬的思路更简单:
先从左向右遍历,右边大于左边就+1;
然后从右向左遍历,左边大于右边就取max(左边孩子的糖果数,右边孩子的糖果数+1)。
简单的思路很难理出来,反正这里就是抓住关键点,一个是每人至少1个糖,一个是只和相邻的比较。
class Solution {
public:
/**
* pick candy
* @param arr int整型vector the array
* @return int整型
*/
int candy(vector<int>& arr) {
// write code here
int m=arr.size();
vector<int> can(m,1);
for(int i=1;i<m;i++)
{
if(arr[i]>arr[i-1]) can[i]=can[i-1]+1;//首先用当前的和前一位得分相比,确保右边得分高的总比左边糖果数多
}
for(int i=m-2;i>=0;i--)
{
if(arr[i]>arr[i+1]) can[i]=max(can[i],can[i+1]+1);//用当前和后一位相比,取自己和后一位+1的最大值,保障左边得分高的也比右边糖果数多
}
int candy=0;
for(int i=0;i<m;i++)
{
candy+=can[i];
}
return candy;
}
};
1.6 跳格子
本来思路:
从0索引开始,在nums[0]范围内找最大值max,那么下一次就从最大值开始找nums[max]范围中的最大值,依次类推,直到索引到达倒数第二个。
但这样会超时。
所以优化方法是,只遍历一次,然后找每个索引处可以到达的最大索引maxPos
,同时设置边界end
,每当遍历到边界的时候,就是要跳一次的时候,step+1
,然后重新设置end=maxPos
。
class Solution {
public:
int jump(vector<int>& nums) {
#if 0
int step=0,mmax,ns;
for(int i=0;i<nums.size()-1;)
{
mmax=0;
int j=1;
while(j<=nums[i])
{
if(nums[i+j]>mmax) ns=i+j;
}
step++;
i=ns;
}
return step;
#endif
int step=0,end=0,mmax=0;
for(int i=0;i<nums.size()-1;i++)
{
mmax=max(mmax,i+nums[i]);
if(i==end)
{
end=mmax;
step++;
}
}
return step;
}
};