力扣寒假刷题笔记(1)1.22 回文子序列,整数除法

一、删除回文子串

2022/1/22 的每日一题,点开前看着简单还松了一口气,一看题目:好高端,这是简单?我是fw。

最开始的思路是用动态规划dp枚举出所有起点所有长度的回文子序列,这个错误思路和力扣第五题相同。没删完之前一直循环,每次删除最大的一个回文子串,也就是每次都要重新对新的字串进行dp。

第五题利用的一点是:如果一个长度大于2的字串是回文串,那么去掉首尾的两个字符剩下的字串也是回文串。

动态规划一直不熟练,想不到,不会用,寒假能搞明白就谢天谢地。

 

#include <iostream>
#include <vector>
#include <string>
using namespace std;
class Solution {
public:
    string longestPalindrome(string s) {
        int n = s.size();
        if(n<2) return s;
        int maxlen =1;
        int begin =0 ;
        vector<vector<int>> dp(n,vector<int> (n));
        for(int i=0;i<n;i++) dp[i][i] =true;
        for(int L=2;L<=n;L++)
        {
            for (int i = 0; i < n; i++) {
                int j = L + i - 1;
                if (j >= n) {
                    break;
                }
                if(s[i]!=s[j]) dp[i][j] = false;
                else 
                {
                    if(j-i<3)
                    {
                        dp[i][j] =true;
                    }
                    else dp[i][j] = dp[i+1][j-1];
                }
                if(dp[i][j]&&j-i+1>maxlen)
                {
                    maxlen = j-i+1;
                    begin = i;
                }
            }
        }
        return s.substr(begin, maxlen);
    }
};

回到删除这一题,要注意题目给的是删除回文子序列,没有说回文子串,删除的字符是一个回文就行了,没有一定要凑一起的限制。当时是忽略了题目限定字符串只有a,b。正确思路是: 如果给定的字串本身是回文子序列,删一次就行了,如果不是那么先删完a,再删b,或者反过来就行。

class Solution {
public:
    int removePalindromeSub(string s) {
        int n=s.size();
        for(int i=0;i<n/2;i++)
        {
            if(s[i]!=s[n-i-1]) return 2;
        }
        return 1;
    };
};

二、整数除法

用减法代替除法。

class Solution {
public:
    int divide(int a, int b) {
        if(b==0) return 0;
        if (a == INT_MIN){ //处理溢出的情况
            if (b == -1) return INT_MAX;
        }
        int flag = (a>0^b>0)? -1:1;//确定最后结果的符号
        //a,b统一转换成负数,避免溢出
        if(a>0) a=-a;
        if(b>0) b=-b;
        unsigned int res=0;
        while(a<=b) 
        {
            int value = b;
            unsigned int k = 1;
            while (value >= 0xc0000000 && a <= value + value) {
                k += k;
                value += value;
            }
            a-=value;
            res+=k;
        }
        return flag==1 ? res:-res;
    }
};

每次尝试去减去b的倍数,以22/3为例,22-(3+3),22-(6+6) 

三、关押罪犯 二分图,二分搜索

S 城现有两座监狱,一共关押着 N 名罪犯,编号分别为 1∼N1∼N。

他们之间的关系自然也极不和谐。

很多罪犯之间甚至积怨已久,如果客观条件具备则随时可能爆发冲突。

我们用“怨气值”(一个正整数值)来表示某两名罪犯之间的仇恨程度,怨气值越大,则这两名罪犯之间的积怨越多。

如果两名怨气值为 c 的罪犯被关押在同一监狱,他们俩之间会发生摩擦,并造成影响力为 c 的冲突事件。

每年年末,警察局会将本年内监狱中的所有冲突事件按影响力从大到小排成一个列表,然后上报到 S 城 Z 市长那里。

公务繁忙的 Z 市长只会去看列表中的第一个事件的影响力,如果影响很坏,他就会考虑撤换警察局长。

在详细考察了 N 名罪犯间的矛盾关系后,警察局长觉得压力巨大。

他准备将罪犯们在两座监狱内重新分配,以求产生的冲突事件影响力都较小,从而保住自己的乌纱帽。

假设只要处于同一监狱内的某两个罪犯间有仇恨,那么他们一定会在每年的某个时候发生摩擦。

那么,应如何分配罪犯,才能使 Z 市长看到的那个冲突事件的影响力最小?这个最小值是多少?

输入格式

第一行为两个正整数 N 和 M,分别表示罪犯的数目以及存在仇恨的罪犯对数。

接下来的 MM 行每行为三个正整数 aj,bj,cj

aj,bj,cj,表示 aj 号和 bj 号罪犯之间存在仇恨,其怨气值为 cj。

数据保证 1≤aj<bj<N,0<cj≤10^9 1≤aj<bj<N,0<cj≤10^9 且每对罪犯组合只出现一次。

输出格式

输出共 11 行,为 ZZ 市长看到的那个冲突事件的影响力。

如果本年内监狱中未发生任何冲突事件,请输出 00。

数据范围

N≤20000,M≤100000

 说的就是把一个图的结点分成两组,使得两组间边的权值最小,大于这个最小值的边都要在组内。

 隐含的二分思路就是小于等于这个最小值的无法被分成两组。

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=20010,M=200010;
int h[N],e[M],ne[M],w[M],idx;
int st[N];
int n,m;
void add(int a,int b,int c)
{
    w[idx]=c;
    e[idx]=b;
    ne[idx]=h[a];
    h[a]=idx++;
}
bool dfs(int u,int c,int lim)
{
    st[u]=c;
    for(int i=h[u];i!=-1;i=ne[i])
    {
        if(w[i]<=lim) continue;
        int j=e[i];
        if(!st[j])
        {
            if(!dfs(j,3-c,lim)) return false;
        }
        else if(st[j]==c) return false;
    }
    return true;
}
bool check(int mid)
{
    memset(st,0,sizeof st);
    for(int i=1;i<=n;i++)
    {
        if(!st[i])
        {
            if(!dfs(i,1,mid)) return false;
        }
    }
    return true;
}
int main()
{
    memset(h,-1,sizeof h);
    cin >>n>>m;
    while(m--)
    {
        int a,b,c;
        cin >>a>>b>>c;
        add(a,b,c);
        add(b,a,c);
    }
    int l=0,r=1e9;
    while(l<r)
    {
        int mid=l+r>>1;
        if(check(mid)) r=mid;
        else l=mid+1;
    }
    cout<<l;
    return 0;
}

 记录一下二分查找模板,这个模板老忘。

//二分查找模板
//(1)check=true 条件是>=target
    while (l < r) {
      int mid = l + r >> 1;
      if (a[mid] >= k) {
        r = mid;  //第二种情况 找绿色区间的左端点
      } else {
        l = mid + 1;
      }
    }
//(2)check=true 条件是<=target
    while (l < r) {
        int mid = l + r + 1 >> 1;
        if (a[mid] <= k) {//第一种情况 找红色区间的右端点
          l = mid;
        } else {
          r = mid - 1;
        }
    }

 记录一下染色法判断二分图模板。

#include <cstring>
#include <iostream>
using namespace std;
const int N=1e5 + 10, M = 2e5 + 10;;
int e[M], ne[M], h[N];
int n,m,idx;
int color[N];
void add(int a,int b)
{
    e[idx]=b;
    ne[idx]=h[a];
    h[a]=idx++;
}
bool dfs(int u,int c)
{
    color[u]=c;
    for(int i=h[u];i!=-1;i=ne[i])
    {
        int j=e[i];
        if(!color[j])
        {
            if(!dfs(j,3-c)) return false;
        }
        else 
        {
            if(color[j]==c) return false;
        }
    }
    return true;
}
int main()
{
    cin >>n>>m;
    memset(h,-1,sizeof h);
    while(m--)
    {
        int a,b;
        cin>>a>>b;
        add(a,b);
        add(b,a);
    }
    bool flag=true;
    for(int i=1;i<=n;i++)
    {
        if(!color[i])
        {
            if(!dfs(i,1)) 
            {
                flag=false;
                break;
            }
        }
    }
    if(flag) puts("Yes");
    else puts("No");
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值