LA --- 2678 子序列 【思维】

来源:互联网 发布:Linux解压rar 编辑:程序博客网 时间:2024/06/06 01:35

传送门
//题意就是从给定的一段序列中找最短的连续子列, 使得他们的和大于等于S.
//这是白书上一些优化算法的引入, 从开始的O(n^3)逐渐到最后的O(n), 引入思考是很棒的. 也就是我们首先需要构造前缀和, 然后确定目标, 设sum[i]表示前缀和. 那么我们的目的是找sum[j] - sum[i-1] >= S. 且i要尽量的大. 也就是sum[i-1] <= sum[j] - S. 然后就有一个O(n)的算法实现 :

AC Code

/** @Cain*/const int maxn=1e5+5;int cas=1;int sum[maxn];void solve(){    int n,s;    while(~scanf("%d%d",&n,&s)){        Fill(sum,0);        for(int i=1;i<=n;i++){            int u; scanf("%d",&u);            sum[i] = sum[i-1] + u;        }        int res = inf;        int i = 1;        for(int j=1;j<=n;j++){            if(sum[i-1] > sum[j] - s) continue; //不符合直接continue            while(sum[i] <= sum[j] - s) i++;            //注意我这是先判断,再加加,所以应该写sum[i],而不是sum[i-1].            //随便试一组样例就可以知道.            res = min(res,j-i+1);        }        printf("%d\n",res==inf?0:res);    }}

//为什么是O(n)了, 因为从那个循环中可以看出来 i,j 实际上都是只遍历了一遍的, 所以复杂度就是O(n)的.

给出一个O(n^2)的写法(不是针对这道题, 而是其他一些类似的题!!!), 因为这个是经常用到的. O(n)是因为前缀和是满足递增的状态, 所以才可以这样写. 而一些子序列求的不是子串的和, 而是子串的异或值, 积, gcd等, 此时最差也要用n^2的做法, 并且每个运算, 求区间时进行的操作也是不同的, 不要老是想着和 !!! 根据不用的操作进行不同的处理.

/** @Cain*/const int maxn=1e5+5;int cas=1;int sum[maxn];void solve(){    int n,s;    while(~scanf("%d%d",&n,&s)){        Fill(sum,0);        for(int i=1;i<=n;i++){            int u; scanf("%d",&u);            sum[i] = sum[i-1] + u;        }        int res = inf;        int i = 1;        for(int i=1;i<=n;i++){            for(int j=i;j<=n;j++){                int tmp = sum[j] - sum[i-1];                if(tmp>=s) res = min(res,j-i+1);            }        }        printf("%d\n",res==inf?0:res);    }}

//如果是询问的状态, 那么可以进行预处理, 更省时间!!! 即处理出每一个可能的区间值和对应的长度.

原创粉丝点击