ACM训练日记—4月10日

       先回顾下之前做过的几道题,在整理下一些老知识点吧。

1,最长公共子序列(LCS)

     (1)a[i]=a[j]时,dp[i][j]=dp[i-1][j-1]+1;

     (2)a[i]!=a[j]时,dp[i][j]=max(dp[i-1][j],dp[i][j-1]); 

     (3)i=0||j=0时, dp[i][j]=0;

 上面都是老知识点,这个算法是O(n*m)的,而优化最快是O(nlog n)。

 来自:https://blog.csdn.net/non_cease/article/details/6918848 

 1. 转化:将LCS问题转化成LIS问题。

         假设有两个序列 s1[ 1~6 ] = { a, b, c , a, d, c }, s2[ 1~7 ] = { c, a, b, e, d, a, b }。记录s1中每个元素在s2中出现的位置, 再将位置按降序排列, 则上面的例子可表示为:loc( a)= { 6, 2 }, loc( b ) = { 7, 3 }, loc( c ) = { 1 }, loc( d ) = { 5 }。将s1中每个元素的位置按s1中元素的顺序排列成一个序列s3 = { 6, 2, 7, 3, 1, 6, 2, 5, 1 }。
之所以是nlog n,就是因为转化为LIS(n log n)了。

2,最长上升子序列(LIS)

    来自:https://blog.csdn.net/shuangde800/article/details/7474903

最长上升子序列(LIS)的典型变形,熟悉的n^2的动归会超时。LIS问题可以优化为nlogn的算法。
定义d[k]:长度为k的上升子序列的最末元素,若有多个长度为k的上升子序列,则记录最小的那个最末元素。
注意d中元素是单调递增的,下面要用到这个性质。
首先len = 1,d[1] = a[1],然后对a[i]:若a[i]>d[len],那么len++,d[len] = a[i];
否则,我们要从d[1]到d[len-1]中找到一个j,满足d[j-1]<a[i]<d[j],则根据D的定义,我们需要更新长度为j的上升子序列的最末元素(使之为最小的)即 d[j] = a[i];
最终答案就是len
利用d的单调性,在查找j的时候可以二分查找,从而时间复杂度为nlogn。

 整理题目:

CodeForces - 216B

题意:有一群人要踢足球,但是在这些人中有几对人存在敌对的关系,要求每个队的人数相等并且同一个队中不存在互为敌对关系的人,每个人最多只会讨厌两个人,如果a讨厌b,那么b也会讨厌a,问你至少有多少人不能参加比赛。  

      并查集找奇数环,代码比较好理解,毕竟最多很只有三人环

 代码:

int sum[110], pa[110];
int n;
int find(int i)
{
    if(pa[i] == i)
        return i;
    else
        return find(pa[i]);
}
int main()
{
    int m;
    int i, j, k;
    scanf("%d%d",&n,&m);
    int ans=n;
    for(int i = 1; i <= n; i ++)
    {
        sum[i] = 1;
        pa[i] = i;
    }
    for(i = 0; i < m; i ++)
    {
        int a, b;
        scanf("%d%d",&a,&b);
        a = find(a);
        b = find(b);
        if(a != b)
        {
            pa[a] = b;
            sum[b] += sum[a];
        }
        else//
        {
            if(sum[a] % 2)
                ans --;
        }
    }
    if(ans%2)
    ans--;
    cout<<n-ans<<endl;
    return 0;
}

CodeForces - 219B

题意:一个物品的价格为p,这个价格最多可以降低d,求[p-d,p]结尾含有最多数字9的最大值。

思路:肯定不能暴力1e18。

 从个位开始变成9,依次向前挪位,毕竟低位上的变成9代价低。然后依次判断就可以了。但是网上一位大佬写的代码超短,值得寻味。

来自:https://blog.csdn.net/zorro980302/article/details/55105329

int main()  
{  
    long long p,d,ans;  
    while(scanf("%I64d%I64d",&p,&d)!=EOF)  
    {  
        ans=++p;//补上多减去的1  
        for(long long temp=10;;temp*=10)  
        {  
            if(p%temp>d) break;//将后n位全部变为0的差值是否在可行范围内  
            ans=p-p%temp;  //注意这里的p一直没变
        }  
        printf("%I64d\n",ans-1);  
    }  
    return 0;  
}  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值