迭代法也称辗转法,是一种不断用变量的旧值递推新值的过程,在解决问题时总是重复利用一种方法。与迭代法相对应的是直接法(或者称为一次解法),即一次性解决问题。迭代法又分为精确迭代和近似迭代。“二分法”和“牛顿迭代法”属于近似迭代法,功能都比较类似。
迭代算法是用计算机解决问题的一种基本方法。它利用计算机运算速度快、适合做重复性操作的特点,让计算机对一组指令(或一定步骤)进行重复执行,在每次执行这组指令(或这些步骤)时,都从变量的原值推出它的一个新值。
在使用迭代算法解决问题时,需要做好如下3个方面的工作。
在可以使用迭代算法解决的问题中,至少存在一个迭代变量,即直接或间接地不断由旧值递推出新值的变量。
迭代关系式是指如何从变量的前一个值推出其下一个值的公式或关系。通常可以使用递推或倒推的方法来建立迭代关系式,迭代关系式的建立是解决迭代问题的关键。
在编写迭代程序时,必须确定在什么时候结束迭代过程,不能让迭代过程无休止地重复执行下去。通常可分为如下两种情况来控制迭代过程:
本题疑似错题,不保证存在靠谱的多项式复杂度的做法。测试数据非常的水,各种做法都可以通过,不代表算法正确。因此本题题目和数据仅供参考。
已知有两个字串 A , B A,B A,B 及一组字串变换的规则(至多 6 6 6 个规则),形如:
规则的含义为:在 A A A 中的子串 A 1 A_1 A1 可以变换为 $ B_1 , , ,A_2$ 可以变换为 B 2 ⋯ B_2\cdots B2⋯。
例如: A = abcd A=\texttt{abcd} A=abcd, B = xyz B=\texttt{xyz} B=xyz,
变换规则为:
则此时, A A A 可以经过一系列的变换变为 B B B,其变换的过程为:
共进行了 3 3 3 次变换,使得 A A A 变换为 B B B。
第一行有两个字符串 A , B A,B A,B。
接下来若干行,每行有两个字符串 A i , B i A_i,B_i Ai,Bi,表示一条变换规则。
若在
10
10
10 步(包含
10
10
10 步)以内能将
A
A
A 变换为
B
B
B,则输出最少的变换步数;否则输出 NO ANSWER!
。
abcd xyz
abc xu
ud y
y yz
3
对于 100 % 100\% 100% 数据,保证所有字符串长度的上限为 20 20 20。
【题目来源】
NOIP 2002 提高组第二题
#include
#include
#include
using namespace std;
string st1;
string st2;
const int maxn=15;
int ok=0;
struct method{
string s1;
string s2;
}p[maxn];
string ss1,ss2;
int cnt;
int d; //迭代加深的深度
void dfs(int cur,string st){
if(cur>d) return; //因为dfs是同层同步的,所以只要有一个触底就全部触底,那么就说明d深度内无法找到答案
for(int i=1;i<=cnt;i++){ //尝试每种操作
int m=-1;
while(true){
m=st.find(p[i].s1,m+1);
if(m==-1) break; //如果find返回-1则说明没有找到
string ss=st;
int len=(p[i].s1).size();
ss.replace(m,len,p[i].s2); //函数的用法和m的处理上面算法分析写得很清楚了,这里就不写了
if(ss==st2){
ok=1;
cout<<cur;
return;
}
if(!ok) dfs(cur+1,ss);
}
}
}
int main(){
cin>>st1>>st2; //读入
while(cin>>ss1>>ss2){
p[++cnt].s1=ss1;
p[cnt].s2=ss2;
}
for(d=1;d<=10 && !ok;d++){
dfs(1,st1);
}
if(!ok) cout<<"NO ANSWER!";
return 0;
}
#include
#include
#include
using namespace std;
string X,Y,A[9],B[9]; int n,ans;
inline string trans(const string& str,int i,int j){
static string ans; ans="";
if(i+A[j].length()>str.length()) return ans;
for(int k=0,mx=A[j].length();k<mx;++k)
if(str[i+k]!=A[j][k]) return ans;
ans=str.substr(0,i),ans+=B[j];
return ans+str.substr(i+A[j].length());
}
inline string check(const string& str,int i,int j){
static string ans; ans="";
if(i+A[j].length()>str.length()) return ans;
for(int k=0,mx=A[j].length();k<mx;++k)
if(str[i+k]!=A[j][k]) return ans;
ans=str.substr(0,i),ans+=B[j];
return ans+str.substr(i+A[j].length());
}
inline bool dfs(string& now,int stp){ //管他重复不重复,一路搜。(当然你也可以自己加剪枝之类的优化)
if(now==Y) return 1;
if(stp>ans) return 0; string tmp;
for(int i=0,mx=now.length();i<n;++i)
for(int j=0;j<mx;++j){
tmp=check(now,j,i);
if(tmp!=""&&dfs(tmp,stp+1)) return 1;
} return 0;
}
int main(){
ios::sync_with_stdio(false),cin>>X>>Y;
while(cin>>A[n]>>B[n]) ++n;
for(ans=1;ans<=10;++ans)
if(dfs(X,1)) return !printf("%d\n",ans);
return !puts("NO ANSWER!");
}
#include
#include
#include
#include
using namespace std;
struct Pri
{
char A[25];
char B[25];
};
int n = 1;
int maxn; //最大深度(名字很奇怪)
Pri P[10];
char А[25], B[25];
int mins = 11;
bool dfs(int s, char* str)
{
//puts(str);
if (s > maxn) return false;//深度限制
if (strcmp(str, B) == 0) return true; //找到解立即返回
for (int i = 1; i <= n; i++)
{
int lΑ = strlen(P[i].А);
int lΒ = strlen(P[i].В);
int lS = strlen(str);
int p, q;
for (p = 0; p < lS; p++)
{
for (q = 0; q < lΑ; q++)
if (str[p + q] != P[i].A[q])
break;
if (q == lA)
{
char tmp[500];
int k = 0;
for (int j = 0; j < p && k < 500; j++) tmp[k++] = str[j];
for (int j = p; j < p + lB && k < 500; j++) tmp[k++] = P[i].B[j - p];
for (int j = p + lA; j < lS && k < 500; j++) tmp[k++] = str[j];
if (k != 500)
{
tmp[k] = '\0';
if (dfs(s + 1, tmp)) return true; //找到解立即返回
}
}
}
}
return false;
}
int main()
{
scanf("%s%s", A, B);
for(; scanf("%s%s", P[n].Α, P[n].В) > 0; n++){}
n--;
for (maxn = 1; maxn <= 10; maxn++)//不断加大深度
{
if(dfs(0, A)) //找到后输出答案
{
printf("%d\n", maxn);
return 0;
}
}
printf("NO ANSWER!");
return 0;
}
Const maxn=10000;
maxq=100000;
Var a:array[0..1,0..maxn]of string;//变换规则
q:array[0..1,0..maxq]of string;//两个队列
step:array[0..1,0..maxn]of longint;//步数
head,tail:array[0..1]of longint;//两个队列的头指针和尾指针
int,aim,s1,s2,s:string;
n:longint;
Procedure split(s:string);//将目标状态和初始状态记录下来
var k:longint;
begin
k:=pos(' ',s);
s1:=copy(s,1,k-1);
s2:=copy(s,k+1,length(s)-k);
end;
Procedure init; //读入
begin
readln(s);
split(s);
int:=s1;//初始状态
aim:=s2;//目标状态
n:=0;
while not eof do
begin
readln(s);
if s='' then exit;
inc(n);
split(s);
a[0,n]:=s1;//初始的可以转换的状态
a[1,n]:=s2;//由初始状态转换一步得到的目标状态
end;
end;
Function vis(s:string;t:byte):boolean;
var i:longint;
begin
vis:=false;
for i:=1 to tail[t] do//遍历队列
if q[t,i]=s then exit(true);//如果找到目标状态就返回值true
end;
Procedure print(k:longint);
begin
writeln(k);//(如果合法)输出最少变换步数
halt;
end;
Procedure check(t:byte);
var i:longint;
begin
for i:=1 to tail[1-t] do //遍历队列(当前的状态保存在队列里)
if q[1-t,i]=q[t,tail[t]] then //如果两个广搜碰头了
print(step[1-t,i]+step[t,tail[t]]);//总的步数就是两个广搜步数之和
end;
Procedure bfs(t:byte); //广搜(t=0是正着搜,t=1是反着搜)
var i,j,k:longint;
pre,tmp:string;
begin
inc(head[t]);//头指针加一
pre:=q[t,head[t]];//入队
for i:=1 to n do//遍历变换规则
begin
k:=length(a[t,i]);
for j:=1 to length(pre)-k+1 do//按照变换规则扩展状态
begin
if copy(pre,j,k)=a[t,i] then//如果规则符合
begin
tmp:=copy(pre,1,j-1)+a[1-t,i]+copy(pre,j+k,length(pre)-j-k+1);//扩展下一个状态
if not vis(tmp,t) then//如果没有找到目标状态
begin
inc(tail[t]);
q[t,tail[t]]:=tmp;
step[t,tail[t]]:=step[t,head[t]]+1;//步数++
end;
check(t);//检查是否终止搜索(注意位置,不然就T了)
end;
end;
end;
end;
Procedure doublebfs;//用数组下标来区分两个队列和两个广搜
begin
head[0]:=0;//第一个队列的头指针
head[1]:=0;//第二个队列的头指针
tail[0]:=1;//第一个队列的尾指针
tail[1]:=1;//第二个队列的尾指针
q[0,1]:=int;//初始状态
q[1,1]:=aim;//目标状态
step[0,1]:=0;//步数
step[1,1]:=0;//步数
while (head[0]<tail[0])and(head[1]<tail[1])do
if tail[1]<tail[0] then
bfs(1) else bfs(0);//保持两个广搜的同步
end;
Begin
init;
doublebfs;
writeln('NO ANSWER!');
End.
本文完。关于迭代(辗转)算法的题还有很多,可以在洛谷上刷。本文由于代码有一些已经加了注释,所以就不在文章里说明了,如果不懂,欢迎在评论区留言。至此,算法入门教程就已经结束了。我为大家讲解了模拟、枚举、递推、递归、分治、贪心、试探(回溯)以及迭代(辗转),总共有8种常见的算法。注意,这只是入门教程,如果要学习更高级的内容,请在CSDN算法技能树学习。