问题求解
小陈现有 2 个任务 A,B 要完成,每个任务分别有若干步骤如下: A = a 1 − > a 2 − > a 3 A=a_1->a_2->a_3 A=a1−>a2−>a3 , B = b 1 − > b 2 − > b 3 − > b 4 − > b 5 B=b_1->b_2->b_3->b_4->b_5 B=b1−>b2−>b3−>b4−>b5 。在任何时候,小陈只能专心做某个任务的一个步骤。但是如果愿意,他可以在做完手中任务的当前步骤后,切换至另一个任务,从上次此任务第一个未做的步骤继续。每个任务的步骤顺序不能打乱,例如 … a 2 − > b 2 − > a 3 − > b 3 … \ldots a_2->b_2->a_3->b_3 \ldots …a2−>b2−>a3−>b3…是合法的,而 … a 2 − > b 3 − > a 3 − > b 2 … \ldots a_2->b_3->a_3->b_2 \ldots …a2−>b3−>a3−>b2…是不合法的。小陈从 B 任务的 b 1 b_1 b1 步骤开始做,当恰做完某个任务的某个步骤后,就停工回家吃饭了。当他回来时,只记得自己已经完成了整个任务 A ,其他的都忘了。试计算小陈饭前已做的可能的任务步骤序列共有(70
)种。
【解析】A任务全部完成,那么可以根据B任务的完成情况分为以下几种:
- 完成了 b 1 b_1 b1,方案只有
1
种- 完成了 b 1 , b 2 b_1,b_2 b1,b2,对A任务的序列可以分为2种情况,可以使用插空法:
- 3个任务是连续完成的,方案有
2
种- 3个任务两个是连续完成的,方案有
2
种- 完成了 b 1 , b 2 , b 3 b_1,b_2,b_3 b1,b2,b3,对A任务的序列可以分为3种情况,可以使用插空法:
- 3个任务是连续完成的,方案有 C 3 1 C_3^1 C31,共
3
种- 3个任务两个是连续完成的,方案 C 3 2 × 2 C_3^2\times2 C32×2,共
6
种- 3个任务是相互分隔开的,方案 C 3 3 C_3^3 C33,共
1
种- 完成了 b 1 , b 2 , b 3 , b 4 b_1,b_2,b_3,b_4 b1,b2,b3,b4,对A任务的序列可以分为3种情况,可以使用插空法:
- 3个任务是连续完成的,方案有 C 4 1 C_4^1 C41,共
4
种- 3个任务两个是连续完成的,方案 C 4 2 × 2 C_4^2\times2 C42×2,共
12
种- 3个任务是相互分隔开的,方案 C 4 3 C_4^3 C43,共
4
种- 完成了 b 1 , b 2 , b 3 , b 4 , b 5 b_1,b_2,b_3,b_4,b_5 b1,b2,b3,b4,b5,对A任务的序列可以分为3种情况,可以使用插空法:
- 3个任务是连续完成的,方案有 C 5 1 C_5^1 C51,共
1
种- 3个任务两个是连续完成的,方案 C 5 2 × 2 C_5^2\times2 C52×2,共
20
种- 3个任务是相互分隔开的,方案 C 5 3 C_5^3 C53,共
10
种
阅读程序
T3
#include <iostream>
using namespace std;
const int c=2009;
int main()
{
int n,p,s,i,j,t;
cin >> n >> p;
s=0;t=1;
for(i=1;i<=n;i++)
{
t=t*p%c;
for(j=1;j<=i;j++)
s=(s+t)%c;
}
cout << s << endl;
return 0;
}
输入:
11 2
输出:
782
【解析】计算:
( 1 × 2 1 + 2 × 2 2 + 3 × 2 3 + 4 × 2 4 + 5 × 2 5 + 6 × 2 6 + 7 × 2 7 + 8 × 2 8 + 9 × 2 9 + 10 × 2 10 + 11 × 2 11 ) m o d 2009 = 782 (1\times2^1+2\times2 ^2+3\times2^3+4\times2^4+5\times2^5+6\times2^6+7\times2^7+8\times2^8+9\times2^9+10\times2^{10}+11\times2^{11})\mod2009=782 (1×21+2×22+3×23+4×24+5×25+6×26+7×27+8×28+9×29+10×210+11×211)mod2009=782
T4
#include <iostream>
#include <cstring>
using namespace std;
const int maxn=50;
void getnext(char str[])
{
int l=strlen(str),i,j,k,temp;
k=l-2;
while(k>=0&&str[k]>str[k+1]) k--;
i=k+1;
while(i<l&&str[i]>str[k]) i++;
temp=str[k];
str[k]=str[i-1];
str[i-1]=temp;
for(i=l-1;i>k;i--)
for(j=k+1;j<i;j++)
if(str[j]>str[j+1])
{
temp=str[j];
str[j]=str[j+1];
str[j+1]=temp;
}
return ;
}
int main()
{
char a[maxn];
int n;
cin >> a >> n;
while(n>0)
{
getnext(a);
n--;
}
cout << a << endl;
return 0;
}
输入:NOIP 3
输出:NPOI
【解析】模拟即可:
- 第1次循环:a = “NOPI”
- 第2次循环:a = “NPIO”
- 第3次循环:a = “NPOI”
完善程序
T1(最大连续子段和)给出一个数列(元素个数不多于 100),数列元素均为负整数、正整数、0。请找出数列中的一个连续子数列,使得这个子数列中包含的所有元素之和最大,在和最大的前提下还要求该子数列包含的元素个数最多,并输出这个最大和以及该连续子数列中元素的个数。例如数列为 4,-5,3,2,4 时,输出 9 和 3;数列为 1,2,3,-5,0,7,8时,输出 16 和 7。
#include <iostream>
using namespace std;
int a[101];
int n,i,ans,len,tmp,beg;
int main(){
cin >> n;
for (i=1;i<=n;i++)
cin >> a[i];
tmp=0;
ans=0;
len=0;
beg= ①;
for (i=1;i<=n;i++){
if (tmp+a[i]>ans){
ans=tmp+a[i];
len=i-beg;
}
else if (②&&i-beg>len)
len=i-beg;
if (tmp+a[i]③){
beg=④;
tmp=0;
}
else
⑤;
}
cout << ans <<""<< len << endl;
return 0;
}
【解析】
- 空①,设置连续子数列的起始位置,后面代码计算长度
len
时使用i - beg
,所以beg
应该从0开始计算,此空填0
。- 空②,打擂台如果发现更大的连续数列和,更新
ans
和长度len
;否则,如果发现相等的连续数列和并且长度更长,则更新长度len
;- 空③、空④,如果连续数列和小于0,则从当前位置开始,重新计算连续数列和,因为对于下一项来说加上一个负数只会导致和更小。空③填
<0
,空④应填i
。- 空⑤,继续累加连续数列的和,此空应填
tmp+=a[i]
。
T2 (国王放置)在 n×m 的棋盘上放置 k 个国王,要求 k 个国王互相不攻击,有多少种不同的放置方法。假设国王放置在第 (x,y) 格,国王的攻击的区域是:(x-1,y-1),(x-1,y),(x-1,y+1),(x,y-1),(x,y+1),(x+1,y-1),(x+1,y),(x+1,y+1)。读入三个数 n,m,k,输出答案。题目利用回溯法求解。棋盘行标号为 0~n-1,列标号为 0~m-1。
#include <iostream>
using namespace std;
int n,m,k,ans;
int hash[5][5];
void work(int x,int y,int tot)
{
int i,j;
if (tot==k)
{
ans++;
return;
}
do
{
while (hash[x][y])
{
y++;
if (y==m)
{
x++;
y=①;
}
if (x==n)
return;
}
for (i=x-1; i<=x+1; i++)
if (i>=0&&i<n)
for (j=y-1; j<=y+1; j++)
if (j>=0&&j<m)
②;
③;
for (i=x-1; i<=x+1; i++)
if (i>=0&&i<n)
for (j=y-1; j<=y+1; j++)
if (j>=0&&j<m)
④;
y++;
if (y==m)
{
x++;
y=0;
}
if (x==n)
return;
}while (1);
}
int main()
{
cin >> n >> m >> k;
ans=0;
memset(hash,0,sizeof(hash));
⑤;
cout << ans << endl;
return 0;
}
【解析】题目中通过回溯算法,深度优先搜索放置国王的方案。
hash[i][j] = 0
表示棋盘(i,j)位置在当前分支可以放置国王。
- 空①,如果
hash[x][y]
不为0,表示此位置无法放置国王,那么沿着当前位置从左到右、从上到下找到一个可以放置国王的位置(x,y)。当走到当前行的最后一列,换到下一行第一列开始继续找。- 空②,将国王放置在第 (x,y) 格,国王的攻击的区域是:(x-1,y-1),(x-1,y),(x-1,y+1),(x,y-1),(x,y+1),(x+1,y-1),(x+1,y),(x+1,y+1),将这8个位置的
hash
值全部加1,保证沿当前分支继续搜索时不能在这些位置继续放置国王。此空应填hash[x][y]++
。- 空③,将国王放置在(x,y)位置,沿当前分支继续深度优先搜索下一个放置国王的位置,此空应填
work(x, y, tot+1)
。- 空④,回溯时恢复现场,将
hash
值减1,此空应填hash[x][y]--
。- 空⑤,从棋盘(0,0)位置开始搜索答案,此时放置了0个国王,此空应填
work(0,0,0)
。