求从0到n-1,每个点经过一次的最短路径
#include
using namespace std;
const int N = 20 + 1;
int f[1<
当n为偶数时 n xor 1 = n+1
当n为奇数时 n xor 1 = n-1
用于取邻接表的正反边。
设差分数组 d[i],= a[i] - a[i-1]; 则有
s u m 1 i d [ i ] = a [ i ] sum_{1}^{i}d[i] = a[i] sum1id[i]=a[i]
d[L] += c,d[R+1] -= c;
就可以得出a的l–r减去c的答案
while(l != r)
{
int mid = (l + r)>>1;
if(a[mid] >= x) r = mid;else l = mid+1;
}
while(l != r)
{
int mid = (l + r + 1) >>1;
if(a[mid]] <= x)l = mid;else r = mid-1;
}
while(l != r)
{
int mid = (l + r) >> 1;
if(a[mid] == x) ans = mid;
else
{
if(a[mid] > x) r = mid-1;else l = mid+1;
}
}
sort(b+1,b+1+n);
m = unique(b+1,b+1+n)-b-1;
for (int i = 1;i <= n;i++)
a[i] = lower_bound(b+1,b+1+m,a[i])-b;
p = 1,k = 0,sum = 0;
while(p)
{
if(sum + s[R+p] - s[L] <= T)
sum += s[k+p],R += p,P <<= 1;
else p >>= 1;
}
找到从左到右第一个比i大或小的值的位置;
tac[]存下标,f[]存下标,tac中单调递增,
f[]是从左到右第一个比i大或小的值的位置;
int t = 0;
for (int i = 1;i <= n;i++)
{
while(t && a[tak[t]] <= a[i]) --t;
if(t) f[i] = tac[t];
else f[i] = i;
tak[++t] = i;
}
int l = 1,r = 1;
q[1] = 0;
for (int i = 1;i <= n;i++)
{
while(l <= r && q[l] < i - m) l++;
ans = max(ans,sum[i] - sum[q[l]]);
while(l <= r && sum[q[r]] >= sum[i]) r--;
q[++r] = i;
}
while(t1 < lent)
{
if(t2 == -1 || t[t1] == t[t2]) nxt[++t1] = ++t2;
else t2 = nxt[t2];
}
int i = 0,j = 0;
while(i < lens)
{
if(j == -1 || s[i] == t[j])i++,j++;
else j = nxt[j];
if(j == lent){j = nxt[j];}
}
一. 动态规划
最长上升子序列:
#include
using namespace std;
const int N = 1e3 + 10;
int f[N],n,a[N],ans;
int main()
{
freopen("c.in","r",stdin);freopen("c.out","w",stdout);
scanf("%d",&n);
for (int i = 1;i <= n;i++)
{
scanf("%d",&a[i]);
}
for (int i = 1;i <= n;i++)
{
f[i] = 1;
for (int j = 1;j < i;j++)
if(a[i] > a[j] && f[i] < f[j]+1)
f[i] = f[j] + 1;
ans = max(ans,f[i]);
}
for (int i = 1;i <= n;i++)
{
cout<
·变式「一」
题目描述
擦掉数列中某些数后,剩下的数列中在自己位置上的数尽量多。
输入输出格式
输入格式:
第一行为一个数n,表示数列的长度。
接下来一行为n个用空格隔开的正整数,第i行表示数Ai。
输出格式:
一行一个整数,表示擦掉某些数后剩下的数列中最多能有多少个数在自己的位置上
即Ai=i最多能有多少。
输入输出样例
输入样例 #1:
5
1 1 2 5 4
输出样例 #1:
3
说明
对于20%的数据,n≤20;
对于60%的数据,n≤100;
对于100%的数据,n≤l000。
最后在自己位置上的数字有一个特点:
即是递增的,因为标号递增。
但不完全是最长上升子序列。
两个数字间可以通过删数使他们到自己的位置上的一个充要条件是 a[i] - a[j] <= i-j
因为删数只能使它们之间的距离减小,如果原本就超过指定距离(i-j)则不能通过删数达成
由此得出方程 f [ i ] = m a x ( f [ j ] ) a [ i ] > a [ j ] , i > j , a [ i ] − a [ j ] < = i − j f[i] = max(f[j])_{a[i] > a[j] ,i > j, a[i]-a[j] <= i-j} f[i]=max(f[j])a[i]>a[j],i>j,a[i]−a[j]<=i−j
#include
using namespace std;
const int N = 1e3 + 10;
int f[N],n,a[N],ans;
int main()
{
scanf("%d",&n);
for (int i = 1;i <= n;i++) scanf("%d",&a[i]);
for (int i = 1;i <= n;i++)
{
f[i] = 1;
for (int j = 1;j < i;j++)
if(a[i] > a[j] && a[i] - a[j] <= i-j) f[i] = max(f[i],f[j] + 1);
ans = max(ans,f[i]);
}
printf("%d\n",ans);
return 0;
}
O ( n 3 ) O(n^3) O(n3)
for (int i = 1;i <= n;i++)
for (int j = 1;j <= m;j++)
{
if(a[i] == b[j])//满足公共
{
for (int k = 0; k < j;k++)
if(b[k] < a[i])//满足上升
f[i][j] = max(f[i][j],f[i-1][k] + 1);
}
else f[i][j] = f[i-1][j];
}
O ( n 2 ) O(n^2) O(n2)
考虑到最外层i是固定的,所以for k 0 -> j ,在j+1的时候,只有j+1,进入了决策集合,而集合是不减的,所以只需o(1)判断是否可以加入即可
for (int i = 1;i <= n;i++)
{
int v = 0;
if(b[0] < a[i]) v = f[i-1][0];//j == 1时,0可以作为k的取值。
for (int j = 1;j <= m;j++)
{
if(a[i] == b[j])f[i][j] = v + 1;
else f[i][j] = f[i-1][j];
if(a[i] > b[j]) v = max(v,f[i-1][j]);
}
}