先回顾下之前做过的几道题,在整理下一些老知识点吧。
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;
}