noip知识点总结

基本算法

( 1 )位运算

基本运算
状压
运用:状压dp

求从0到n-1,每个点经过一次的最短路径

#include<bits/stdc++.h>
using namespace std;
const int N = 20 + 1;
int f[1<<N][N];
int c[N][N];
int main()
{
	freopen("c.in","r",stdin);freopen("c.out","w",stdout);
	int n,m;
	scanf("%d%d",&n,&m);
	for (int i = 1;i <= m;i++)
	{
		int x,y,d;
		scanf("%d%d%d",&x,&y,&d);
		x--;y--;
		c[x][y] = c[y][x] = d;
	}
	memset(f,19,sizeof(f));
	f[1][0] = 0;
	for (int i = 1;i < (1<<n);i++)
	{
		for (int j = 0;j < n;j++) if(i & (1<<j))
			for (int k = 0;k < n;k++) if((i^(1<<j))&(1<<k))
			f[i][j] = min(f[i][j],f[i^(1<<j)][k] + c[j][k]);
	}
	printf("%d\n",f[(1<<n)-1][n-1]);
}
成对运算:

当n为偶数时 n xor 1 = n+1
当n为奇数时 n xor 1 = n-1
用于取邻接表的正反边。

(2)枚举,模拟,递推

前缀和 (差分数组)

设差分数组 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的答案

(3)二分 : 务必使用配套的mid取法。

查找x的后继
while(l != r)
{
	int mid = (l + r)>>1;
	if(a[mid] >= x) r = mid;else l = mid+1;
}
查找x的前趋
while(l != r)
{
	int mid = (l + r + 1) >>1;
	if(a[mid]] <= x)l = mid;else r = mid-1;
}
查找x
 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;

(4) 倍增

RMQ
其他应用:
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;
}

(5)贪心

(6)单调栈

	找到从左到右第一个比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;
}

kmp

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<bits/stdc++.h>
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<<f[i]<<" ";
	}
	cout<<endl;
	printf("%d\n",ans);
	return 0;
}

·变式「一」
题目描述
擦掉数列中某些数后,剩下的数列中在自己位置上的数尽量多。

输入输出格式
输入格式:
第一行为一个数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 ] &gt; a [ j ] , i &gt; j , a [ i ] − a [ j ] &lt; = i − j f[i] = max(f[j])_{a[i] &gt; a[j] ,i &gt; j, a[i]-a[j] &lt;= i-j} f[i]=max(f[j])a[i]>a[j],i>j,a[i]a[j]<=ij

#include<bits/stdc++.h>
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) On3

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]);
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

徐行tag

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值