NOIP专题知识点1 dp

动态规划 编辑距离问题

fzoj1307
题目描述
设A 和B 是2 个字符串。要用最少的字符操作将字符串A 转换为字符串B。这里所说的
字符操作包括
(1)删除一个字符;
(2)插入一个字符;
(3)将一个字符改为另一个字符。
将字符串A变换为字符串B 所用的最少字符操作数称为字符串A到B 的编辑距离,记为
d(A,B)。试设计一个有效算法,对任给的2 个字符串A和B,计算出它们的编辑距离d(A,B)。
编程任务:
对于给定的字符串A和字符串B,编程计算其编辑距离d(A,B)。
输入
第一行是字符串A,第二行是字符串B。
输出
输出一行,即将编辑距离d(A,B)输出。
样例输入
fxpimu
xwrs
样例输出
5
提示
两个字符串长度均不超过2000

solution

就是普通的dpdpdp
dp[i][j]dp[i][j]dp[i][j]为用最少的字符操作将字符串A的前i位 转换为字符串B的前j位
操作1:dp[i][j]=dp[i−1][j]+1dp[i][j]=dp[i-1][j]+1dp[i][j]=dp[i1][j]+1
操作2:dp[i][j]=dp[i][j−1]+1dp[i][j]=dp[i][j-1]+1dp[i][j]=dp[i][j1]+1
操作3:dp[i][j]=dp[i−1][j−1]+(a[i]!=b[j])dp[i][j]=dp[i-1][j-1]+(a[i]!=b[j])dp[i][j]=dp[i1][j1]+(a[i]!=b[j])
最后取个minminmin就是dp[i][j]dp[i][j]dp[i][j]
上代码

#include<cstdio>
#include<cstring>
char a[2002],b[2002];
int la,lb,dp[2002][2002];
int minn(int a, int b){return a<b?a:b;}
int main()
{
	gets(a);gets(b);
	la=strlen(a);lb=strlen(b);
	for(int i=0;i<=la;i++)dp[i][0]=i;
	for(int i=0;i<=lb;i++)dp[0][i]=i;
	for(int i=1;i<=la;i++)
	{
		for(int j=1;j<=lb;j++)dp[i][j]=minn(minn(dp[i-1][j]+1,dp[i][j-1]+1),dp[i-1][j-1]+(a[i-1]!=b[j-1]));
	}
	printf("%d\n",dp[la][lb]);
}

[Usaco2004 Nov]Apple Catching 接苹果

fzoj2321
题目描述
很少有人知道奶牛爱吃苹果。农夫约翰的农场上有两棵苹果树(编号为1和2),每一棵树上都长满了苹果。奶牛贝茜无法摘下树上的苹果,所以她只能等待苹果从树上落下。但是,由于苹果掉到地上会摔烂,贝茜必须在半空中接住苹果(没有人爱吃摔烂的苹果)。贝茜吃东西很快,所以她接到苹果后仅用几秒钟就能吃完。
每一分钟,两棵苹果树其中的一棵会掉落一个苹果。贝茜已经过了足够的训练,只要站在树下就一定能接住这棵树上掉落的苹果。同时,贝茜能够在两棵树之间快速移动(移动时间远少于1分钟),因此当苹果掉落时,她必定站在两棵树其中的一棵下面。此外,奶牛不愿意不停地往返于两棵树之间,因此会错过一些苹果。
苹果每分钟掉落一个,共T(1<=T<=1000)分钟,贝茜最多愿意移动W(1<=W<=30)次。现给出每分钟掉落苹果的树的编号,要求判定贝茜能够接住的最多苹果数。开始时贝茜在1号树下。
输入
第1行:由空格隔开的两个整数:T和W;
第2…T+1行:1或2(每分钟掉落苹果的树的编号)。
输出
一行一个数,表示在贝茜移动次数不超过W的前提下她能接到的最多苹果数。
样例输入
7 2
2
1
1
2
2
1
1
样例输出
6
提示
输入说明:7分钟内共掉落7个苹果——第1个从第2棵树上掉落,接下来的2个苹果从第1棵树上掉落,再接下来的2个从第2棵树上掉落,最后2个从第1棵树上掉落。
输出说明:贝茜不移动直到接到从第1棵树上掉落的两个苹果,然后移动到第2棵树下,直到接到从第2棵树上掉落的两个苹果,最后移动到第1棵树下,接住最后两个从第1棵树上掉落的苹果。这样贝茜共接住6个苹果。

solution

dp[i][j]dp[i][j]dp[i][j]表示贝茜在时间i时站在树j下
那么dp[i][j]dp[i][j]dp[i][j]有两种情况 一种是上一分钟在jjj树下 另一种是上一分钟在3−j3-j3j树下
所以
dp[i][j]=(a[i]=j%&ThinSpace;2+1)+max{dp[i−1][j],dp[i−1][j−1]}dp[i][j]=(a[i]=j\%\,2+1)+max\{dp[i-1][j],dp[i-1][j-1]\}dp[i][j]=(a[i]=j%2+1)+max{dp[i1][j],dp[i1][j1]}

#include<cstdio>
int t,w,x,ans,a[1001],dp[1001][31];
int maxx(int a, int b){return a>b?a:b;}
int main()
{
	scanf("%d%d",&t,&w);
	for(int i=1;i<=t;i++)scanf("%d",&a[i]);
	for(int i=1;i<=t;i++)dp[i][0]=dp[i-1][0]+2-a[i];
	for(int i=1;i<=t;i++)
	{
		for(int j=1;j<=w&&j<=i;j++)
		{
			if(j==0)
			{
				if(a[i]==1)dp[i][j]++;
				continue;
			}
			dp[i][j]=(a[i]==(j%2+1))+maxx(dp[i-1][j],dp[i-1][j-1]);
		}
	}
	for(int i=0;i<=w;i++)ans=maxx(ans,dp[t][i]);
	printf("%d\n",ans);
}

NOIP2014 飞扬的小鸟

fzoj1650
题目描述
在这里插入图片描述
输入
第 1 行有 3 个整数 n,m,k,分别表示游戏界面的长度,高度和水管的数量,每两个整数之间用一个空格隔开;

接下来的 n 行,每行 2 个用一个空格隔开的整数 X 和 Y,依次表示在横坐标位置 0~n-1上玩家点击屏幕后,小鸟在下一位置上升的高度 X,以及在这个位置上玩家不点击屏幕时,小鸟在下一位置下降的高度 Y。

接下来 k 行,每行 3 个整数 P,L,H,每两个整数之间用一个空格隔开。每行表示一个管道,其中 P 表示管道的横坐标,L 表示此管道缝隙的下边沿高度为 L,H 表示管道缝隙上边沿的高度(输入数据保证 P 各不相同,但不保证按照大小顺序给出)。
输出
第一行,包含一个整数,如果可以成功完成游戏,则输出 1,否则输出 0。

第二行,包含一个整数,如果第一行为 1,则输出成功完成游戏需要最少点击屏幕数,否则,输出小鸟最多可以通过多少个管道缝隙。
样例输入
10 10 6
3 9
9 9
1 2
1 3
1 2
1 1
2 1
2 1
1 6
2 2
1 2 7
5 1 5
6 3 5
7 5 8
8 7 9
9 1 3
样例输出
1
6
提示

solution

首先是初始化 没有柱子的地方默认柱子下端是000上端是m+1m+1m+1
然后来到了跳跃的问题 每次跳可以向上跳和向下掉 但只能跳和掉二选一 跳能一直跳掉只能掉一次
这…这不是混合背包吗![震惊] 而且向上是完全背包 向下是0−10-101背包
所以向下的方程是dp[i][j]=min{dp[i][j],dp[i−1][j−y[i−1]]}dp[i][j]=min\{dp[i][j],dp[i-1][j-y[i-1]]\}dp[i][j]=min{dp[i][j],dp[i1][jy[i1]]}
向上的方程是dp[i][j]=min{dp[i−1][j−kx[i−1]]+k]},k∈[1,⌊jx[i−1]⌋]dp[i][j]=min\{dp[i-1][j-kx[i-1]]+k]\} ,k\in[1,\left \lfloor \frac{j} {x[i-1]} \right \rfloor]dp[i][j]=min{dp[i1][jkx[i1]]+k]}k[1,x[i1]j]…①
这时取dp[i][j−x[i−1]dp[i][j-x[i-1]dp[i][jx[i1]
dp[i][j−x[i−1]]=min{dp[i−1][j−(k−1)x[i−1]+(k−1)},k∈[1,⌊j−x[i−1]x[i−1]⌋]dp[i][j-x[i-1]]=min\{dp[i-1][j-(k-1)x[i-1]+(k-1)\},k\in[1,\left \lfloor \frac{j-x[i-1]} {x[i-1]} \right \rfloor]dp[i][jx[i1]]=min{dp[i1][j(k1)x[i1]+(k1)}k[1,x[i1]jx[i1]]…②
由①式−-②式得 dp[i][j]=dp[i][j−x[i−1]]+1,k≥2dp[i][j]=dp[i][j-x[i-1]]+1,k\ge2dp[i][j]=dp[i][jx[i1]]+1k2 ((( kkk 是否大于2好像已经没关系了… )))
另外需要注意的细节:
1.不要忘了把dp[0][0]dp[0][0]dp[0][0]的初始值也设为inf
2.输入数组x,yx,yx,y是从000n−1n-1n1 而初始化管子是从111nnn的 第一次就是这么错的…
3.每次算完dp[i][0...m]dp[i][0...m]dp[i][0...m]后 要遍历一遍把管子覆盖的区域的dpdpdp值都设为inf
O(m)O(m)O(m)处理初始化 O(nm)O(nm)O(nm)dpdpdp 总复杂度O(nm)O(nm)O(nm)没问题
细节见代码

#include<cstdio>
inline void read(int &x)
{
	int s=0,w=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){s=(s<<3)+(s<<1)+(ch&15);ch=getchar();}
	x=s*w;
}
int minn(int a, int b){return a<b?a:b;}
const int inf=0x7ffffff;
int n,m,k,now,ans,pipx,x[10001],y[10001],pipedown[10001],pipeup[10001],dp[10001][1001];
int main()
{
	read(n);read(m);read(k);
	for(int i=0;i<n;i++)read(x[i]),read(y[i]);
	for(int i=1;i<=n;i++)pipeup[i]=m+1,pipedown[i]=0;
	for(int i=1;i<=k;i++)
	{
		read(pipx);
		read(pipedown[pipx]);read(pipeup[pipx]);
	}
	for(int i=1;i<=n;i++)for(int j=0;j<=m;j++)dp[i][j]=inf;
	dp[0][0]=inf;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			if(j>=x[i-1])dp[i][j]=minn(minn(dp[i][j],dp[i-1][j-x[i-1]]+1),dp[i][j-x[i-1]]+1);
			if(j==m)for(int k=j-x[i-1];k<=m;k++)dp[i][j]=minn(minn(dp[i][j],dp[i-1][k]+1),dp[i][k]+1);
		}
		for(int j=pipedown[i]+1;j<=pipeup[i]-1;j++)if(j+y[i-1]<=m)dp[i][j]=minn(dp[i][j],dp[i-1][j+y[i-1]]);
		for(int j=1;j<=pipedown[i];j++)dp[i][j]=inf;
		for(int j=pipeup[i];j<=m;j++)dp[i][j]=inf;
	}
	now=k,ans=inf;
	for(int i=n;i;i--)
	{
		for(int j=pipedown[i]+1;j<=pipeup[i]-1;j++)if(dp[i][j]<inf)ans=minn(ans,dp[i][j]);
		if(ans!=inf)break;
		if(pipeup[i]<=m)now--;
	}
	if(now==k)printf("1\n%d\n",ans);
	else printf("0\n%d\n",now);
}

虽然是提高组Day1T3Day1T3Day1T3不过只要理清思路打出代码其实也不难

动态规划 贝茜的晨练计划

fzoj1313
题目描述
奶牛们打算通过锻炼来培养自己的运动细胞,作为其中的一员,贝茜选择的运动方式是每天进行N(1 <= N <= 10,000)分钟的晨跑。在每分钟的开始,贝茜会选择下一分钟是用来跑步还是休息。
贝茜的体力限制了她跑步的距离。更具体地,如果贝茜选择在第i分钟内跑步,她可以在这一分钟内跑D_i(1 <= D_i <= 1,000)米,并且她的疲劳度会增加1。不过,无论何时贝茜的疲劳度都不能超过M(1 <= M <= 500)。如果贝茜选择休息,那么她的疲劳度就会每分钟减少1,但她必须休息到疲劳度恢复到0为止。在疲劳度为0时休息的话,疲劳度不会再变动。晨跑开始时,贝茜的疲劳度为0。
还有,在N分钟的锻炼结束时,贝茜的疲劳度也必须恢复到0,否则她将没有足够的精力来对付这一整天中剩下的事情。
请你计算一下,贝茜最多能跑多少米。
输入
第1行: 2个用空格隔开的整数:N 和 M
第2…N+1行: 第i+1为1个整数:D_i
输出
输出1个整数,表示在满足所有限制条件的情况下,贝茜能跑的最大距离
样例输入
5 2
5
3
4
2
10
样例输出
9
提示
贝茜在第1分钟内选择跑步(跑了5米),在第2分钟内休息,在第3分钟内跑步(跑了4米),剩余的时间都用来休息。因为在晨跑结束时贝茜的疲劳度必须为0,所以她不能在第5分钟内选择跑步

solution

dp[i][j]dp[i][j]dp[i][j]为贝茜在iii分钟用了jjj体力时跑过的最远距离
那么分三种情况
1.下一秒要接着跑:dp[i+1][j+1]=max{dp[i+1][j+1],dp[i][j]+d[i+1]}dp[i+1][j+1]=max\{dp[i+1][j+1],dp[i][j]+d[i+1]\}dp[i+1][j+1]=max{dp[i+1][j+1],dp[i][j]+d[i+1]}
2.从下一秒开始休息:dp[i+j][0]=max{dp[i+j][0],dp[i][j]}dp[i+j][0]=max\{dp[i+j][0],dp[i][j]\}dp[i+j][0]=max{dp[i+j][0],dp[i][j]}
3.当j=0j=0j=0时 还可以接着休息 dp[i+1][0]=max{dp[i+1][0],dp[i][0]}dp[i+1][0]=max\{dp[i+1][0],dp[i][0]\}dp[i+1][0]=max{dp[i+1][0],dp[i][0]}

#include<cstdio> 
int n,m,dp[10010][510],d[10010]; 
int maxx(int a, int b){return a>b?a:b;} 
int main() 
{ 
    scanf("%d%d",&n,&m); 
    for(int i=1;i<=n;i++)scanf("%d",&d[i]); 
    dp[1][0]=0,dp[1][1]=d[1]; 
    for(int i=1;i<=n;i++) 
    { 
        for(int j=0;j<=m;j++) 
        { 
            dp[i+1][j+1]=maxx(dp[i+1][j+1],dp[i][j]+d[i+1]); 
            if(i+j<=n)dp[i+j][0]=maxx(dp[i+j][0],dp[i][j]); 
            if(j==0&&i<n)dp[i+1][0]=maxx(dp[i+1][0],dp[i][0]); 
        } 
    } 
    printf("%d\n",dp[n][0]); 
}

01背包 采药2

fzoj1244
题目描述
辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”
如果你是辰辰,你能完成这个任务吗?

输入
第一行包含两个正整数N,M。M表示总共能够用来采药的时间,N代表山洞里的草药的数目。接下来的N行每行包括两个的整数,分别表示采摘某株草药的时间Ti和这株草药的价值Vi。
输出
包含一个整数表示规定时间内可以采到的草药的最大总价值。
样例输入

3 9
10 10
8 1
1 2

样例输出

3

提示
【数据规模和约定】
50%的数据中 N,M ≤ 1000;
100%的数据中 N,M ≤ 100000,Ti,Vi ≤10。

solution

显然直接01背包会超时
显然我们发现tivi都小于等于10
显然我们应该统计每一种药的数量
显然变成了多重背包
显然我们想出了正解
显然具体详细细节要看代码

#include<cstdio>
inline void read(int &x)
{
	int s=0,w=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){s=(s<<3)+(s<<1)+(ch&15);ch=getchar();}
	x=s*w;
}
int n,m,x,y,nx,ny,now,pnt,med[11][11],dp[100001];
int minn(int a, int b){return a<b?a:b;}
int maxx(int a, int b){return a>b?a:b;}
int main()
{
	read(n),read(m);
	for(int i=1;i<=n;i++)
	{
		read(x),read(y);
		med[x][y]++;
	}
	for(int i=0;i<=10;i++)
	{
		for(int j=0;j<=10;j++)
		{
			x=med[i][j],y=1;
			while(x)
			{
				now=minn(x,y);
				nx=now*i;ny=now*j;
				for(int k=m;k>=nx;k--)dp[k]=maxx(dp[k],dp[k-nx]+ny);
				x-=now;
				y<<=1;
			}
		}
	}
	printf("%d\n",dp[m]);
}

[NOI1995] 石子合并

fzoj2092
题目描述
设有N堆沙子排成一排,其编号为1,2,3,…,N(N<=300)。每堆沙子有一定的数量,可以用一个整数来描述,现在要将这N堆沙子合并成为一堆,每次只能合并相邻的两堆,合并的代价为这两堆沙子的数量之和,合并后与这两堆沙子相邻的沙子将和新堆相邻,合并时由于选择的顺序不同,合并的总代价也不相同,如有4堆沙子分别为 1 3 5 2 我们可以先合并1、2堆,代价为4,得到4 5 2 又合并 1,2堆,代价为9,得到9 2 ,再合并得到11,总代价为4+9+11=24,如果第二步是先合并2,3堆,则代价为7,得到4 7,最后一次合并代价为11,总代价为4+7+11=22;问题是:找出一种合理的方法,使总的代价最小。输出最小代价。
输入
第一行一个数N表示沙子的堆数N。
第二行N个数,表示每堆沙子的质量。 <=1000
输出
合并的最小代价
样例输入
4
1 3 5 2
样例输出
22

solution

这是一道区间dp题
我们用dp[i][j]dp[i][j]dp[i][j]表示合并区间[i,j][i,j][i,j]所需要的最小代价
那么很容易得出关系式dp[i][j]=min{dp[i][j],dp[i][k]+dp[k+1][j]+s[j]−s[i−1]}dp[i][j]=min\{dp[i][j],dp[i][k]+dp[k+1][j]+s[j]-s[i-1]\}dp[i][j]=min{dp[i][j],dp[i][k]+dp[k+1][j]+s[j]s[i1]}

#include<cstdio> 
int n,a[301],s[301],dp[301][301]; 
int minn(int a, int b){return a<b?a:b;} 
int main() 
{ 
    scanf("%d",&n); 
    for(int i=1;i<=n;i++) 
    { 
        scanf("%d",&a[i]); 
        s[i]=s[i-1]+a[i]; 
    } 
    for(int l=1;l<n;l++) 
    { 
        for(int i=1,j=l+1;j<=n;i++,j++) 
        { 
            dp[i][j]=3000001; 
            for(int k=i;k<j;k++)dp[i][j]=minn(dp[i][j],dp[i][k]+dp[k+1][j]+s[j]-s[i-1]); 
        } 
    } 
    printf("%d\n",dp[1][n]); 
}

矩阵

fzoj3641
题目描述
矩阵乘法是定义在矩阵上的二元运算,支持结合律但不支持交换律。一个m*n的矩阵A和一个n*p的矩阵B相乘,需要进行m*n*p次乘法运算,并得到一个m*p的矩阵。
现给出N个矩阵相乘,求进行乘法运算的最小次数。
输入
第一行一个正整数N,表示矩阵的个数。
接下来N行每行两个数m[i]和n[i],表示这个矩阵的行数和列数。
数据保证m[i]=n[i-1],即矩阵可以相乘。
输出
输出一行一个正整数,表示乘法运算的最小次数。
样例输入
3
50 10
10 20
20 5
样例输出
3500
提示
运算顺序为A*(B*C),计算B*C需要10*20*5=1000次乘法,然后得到一个10*5的矩阵BC去和A相乘,乘法次数为50*10*5=2500,总乘法次数为3500.

对于30%的数据,N<=10
对于60%的数据,N<=100
对于100%的数据,2<=N<=500,1<=n[i],m[i]<=50

solution

又是一道区间dpdpdpdp[i][j]dp[i][j]dp[i][j]为第iii个矩阵到第jjj个所用的乘法次数最小值
那么dp[i][j]=min{dp[i][k]+dp[k+1][j]+p[i][0]∗p[k][1]∗p[j][1],k∈[i,j]}dp[i][j]=min\{dp[i][k]+dp[k+1][j]+p[i][0]*p[k][1]*p[j][1],k\in[i,j]\}dp[i][j]=min{dp[i][k]+dp[k+1][j]+p[i][0]p[k][1]p[j][1]k[i,j]}
上代码

#include<cstdio>
int minn(int a, int b){return a<b?a:b;}
int n,p[501][2],dp[501][501];
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d%d",&p[i][0],&p[i][1]);
	for(int l=2;l<=n;l++)
	{
		for(int i=1,j=l;j<=n;i++,j++)
		{
			dp[i][j]=2000000000;
			for(int k=i;k<=j;k++)
			{
				dp[i][j]=minn(dp[i][j],dp[i][k]+dp[k+1][j]+p[i][0]*p[k][1]*p[j][1]);
			}
		}
	}
	printf("%d\n",dp[1][n]);
}

这就是今天的例题 如果有问题可以发到评论区或者加QQ407694747QQ 407694747QQ407694747我们一起讨论
各位大佬各路神犇请多指教

内容概要:本文系统研究了构网型变流器的正负序阻抗解耦特性及其在弱电网环境下的稳定性表现,重点依托Matlab/Simulink仿真平台,构建了详细的阻抗数学模型,设计了解耦控制策略,并采用小信号扫频法进频域辨识与稳定性验证。研究深入探讨了构网型变流器与传统跟网型逆变器在正负序阻抗特性上的本质差异,结合虚拟同步发电机(VSG)等先进控制技术,分析其在抑制宽频带振荡、削弱锁相环动态耦合等方面的优越性。文中不仅提供了完整的仿真模型与MATLAB代实现,还整合了光伏、风电、储能、微电网等多类新能源系统的阻抗建模与稳定性分析资源,形成了一套面向新型电力系统稳定性的综合性技术资料体系,具有较强的科研复现与工程参考价值。; 适合人群:面向具备电力电子、电力系统自动化、新能源并网等专业背景的研究生、高校教师及工程技术人员,特别适用于从事阻抗建模、小干扰稳定性分析、宽频振荡机理研究以及撰写高水平学术论文的科研工作者。; 使用场景及目标:①掌握构网型变流器正负序阻抗建模与扫频辨识的仿真方法;②深入理解VSG等构网型控制在弱电网中提升稳定性的内在机理;③复现顶刊论文中的阻抗分析流程与稳定性判据应用;④利用提供的成熟模型与代加速科研进程,支撑课题研究与学术成果产出。; 阅读建议:建议结合文中提供的Simulink模型与MATLAB代,按照“理论建模—仿真搭建—扫频激励—频响提取—Nyquist判据分析”的完整流程进实践操作,重点关注扫频信号的注入方式、频率范围设置及阻抗曲线的物理意义解读,并参考博士论文复现案例深化对复杂动态耦合问题的理解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值