一、输出全排列
2.样例
输入
3
输出
123
132
213
231
312
321
3.思路
用dfs,将每种情况存储,遍历一个数后标记一下,后面取消标记。
4.代码
#include<bits/stdc++.h>
using namespace std;
int n;
int a[11];
bool vis[11];
void dfs(int t){
if(t==n+1){
for(int i=1;i<=n;i++){
cout<<a[i];
}
cout<<endl;
return ;
}
for(int j=1;j<=n;j++){
if(!vis[j]){
a[t]=j;
vis[j]=1;
dfs(t+1);
vis[j]=0;
}
}
}
int main() {
cin>>n;
dfs(1);
return 0;
}
二、山
1.题目
2.样例
输入
4 5
1 1 0 0 0
1 1 0 0 0
0 0 1 0 0
0 0 0 1 1
输出
3
3.思路
如果这个点是山,向四周遍历,如果四周没山则是高山。如果四周有山则以有山的点向四周遍历,经过的点需要标记。
4.代码
#include<bits/stdc++.h>
using namespace std;
int m,n,sum;
int a[2100][2100];
bool vis[2100][2100];
int b1[4]={-1,1,0,0};
int b2[4]={0,0,-1,1};
void f(int i,int j,int c){
vis[i][j]=1;
if(a[i][j]==0) return ;
int sum1=1;
for(int k=0;k<4;k++){
sum1+=a[i+b1[k]][j+b2[k]];
if(a[i+b1[k]][j+b2[k]]==1&&!vis[i+b1[k]][j+b2[k]]){
f(i+b1[k],j+b2[k],1);
}
}
if(sum1==1&&c==0) sum++;
else if(sum1>1&&c==0) sum++;
}
int main(){
cin>>m>>n;
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
int x;cin>>x;
a[i][j]=x;
}
}
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
if(!vis[i][j]){
f(i,j,0);
}
}
}
cout<<sum;
}
三、跳跃
1.题目
2.样例
输入
7
4 2 3 0 3 1 2
5
输出
True
3.思路
bfs的运用。注意两点一个是出口不是0点,是下标为0的点;二是从0点开始,不能从1点开始存储。
4.代码
#include<bits/stdc++.h>
using namespace std;
int n,start;
int m[510000];
bool vis[510000];
queue<int>q;
int main() {
cin>>n;
for(int i=0;i<n;i++){
cin>>m[i];
}
cin>>start;
q.push(start);
vis[start]=1;
while(!q.empty()){
int x=q.front();
q.pop();
if(m[x]==0){
cout<<"True";
return 0;
}
if(x+m[x]<n&&!vis[x+m[x]])
q.push(x+m[x]),vis[x+m[x]]=1;
if(x-m[x]>=0&&!vis[x-m[x]])
q.push(x-m[x]),vis[x-m[x]]=1;
}
cout<<"False";
return 0;
}
四、最长光路
1.题目
2.样例
输入
5 7
/.....\
../..\.
\...../
/.....\
\.\.../
3 3
输出
R
COOL
3.思路
从光源开始分别向上下左右遍历,找出最大的路径。判断方向的改变需要细心些。
4.代码
#include<bits/stdc++.h>
using namespace std;
int n,m,sx,sy,ans;
char a[501][501];
bool vis[501][501][4];
char d[4]={'U','R','D','L'};
char dans;
int len;
int maxn=0x7f7f7f7f;
void dfs(int x,int y,int dir){
if(vis[x][y][dir]){
len=maxn;
return ;
}
len++;
vis[x][y][dir]=1;
if(a[x][y]=='\\'){
if(dir==0){
dir=3;
y--;
}else if(dir==1){
dir=2;
x++;
}else if(dir==2){
dir=1;
y++;
}else if(dir==3){
dir=0;
x--;
}
}else if(a[x][y]=='/'){
if(dir==0){
dir=1;
y++;
}else if(dir==1){
dir=0;
x--;
}else if(dir==2){
dir=3;
y--;
}else if(dir==3){
dir=2;
x++;
}
}else{
if(dir==0){
x--;
}else if(dir==1){
y++;
}else if(dir==2){
x++;
}else if(dir==3){
y--;
}
}
if(x<1||x>n||y<1||y>m||a[x][y]=='C') return ;
dfs(x,y,dir);
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>a[i][j];
}
}
cin>>sx>>sy;
for(int i=0;i<4;i++){
memset(vis,0,sizeof(vis));
len=0;
dfs(sx,sy,i);
if(len>ans) dans=d[i],ans=len;
}
cout<<dans<<endl;
if(ans==maxn) cout<<"COOL";
else cout<<ans;
return 0;
}
五、回文数文回
1.题目
2.样例
输入
100000001
输出
1
3.思路
先将前5个点列举出来,后面四个点分别与前面四个点保持一致,不大于n的点都是回文数。
4.代码
#include<bits/stdc++.h>
using namespace std;
int n,ans;
int main(){
cin>>n;
for(int i1=1;i1<=9;i1++){
for(int i2=0;i2<=9;i2++){
for(int i3=0;i3<=9;i3++){
for(int i4=0;i4<=9;i4++){
for(int i5=0;i5<=9;i5++){
int sum=i1*1e8+i2*1e7+i3*1e6+i4*1e5+i5*1e4+i4*1e3+i3*1e2+i2*10+i1;
if(sum<=n) ans++;
}
}
}
}
}
cout<<ans;
}
week7
[NOIP2005 普及组] 采药
题目描述
辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”
如果你是辰辰,你能完成这个任务吗?
输入格式
第一行有 2 2 2 个整数 T T T( 1 ≤ T ≤ 1000 1 \le T \le 1000 1≤T≤1000)和 M M M( 1 ≤ M ≤ 100 1 \le M \le 100 1≤M≤100),用一个空格隔开, T T T 代表总共能够用来采药的时间, M M M 代表山洞里的草药的数目。
接下来的 M M M 行每行包括两个在 1 1 1 到 100 100 100 之间(包括 1 1 1 和 100 100 100)的整数,分别表示采摘某株草药的时间和这株草药的价值。
输出格式
输出在规定的时间内可以采到的草药的最大总价值。
样例 #1
样例输入 #1
70 3
71 100
69 1
1 2
样例输出 #1
3
提示
【数据范围】
- 对于 30 % 30\% 30% 的数据, M ≤ 10 M \le 10 M≤10;
- 对于全部的数据, M ≤ 100 M \le 100 M≤100。
【题目来源】
NOIP 2005 普及组第三题
3.思路
如果时间不够用直接进行下一个循环,够用时ans[i][j]=max(ans[i-1][j],ans[i-1][j-t[i]]+v[i]);最后结果为ans[M][T]。
4.代码
#include<bits/stdc++.h>
using namespace std;
int T,M;
int t[1100],v[1100];
int ans[1100][1100];
int main(){
cin>>T>>M;
for(int i=1;i<=M;i++)
cin>>t[i]>>v[i];
memset(ans[0],0,sizeof(ans[0]));
for(int i=1;i<=M;i++){
for(int j=0;j<=T;j++){
if(j<t[i])
ans[i][j]=ans[i-1][j];
else
ans[i][j]=max(ans[i-1][j],ans[i-1][j-t[i]]+v[i]);
}
}
cout<<ans[M][T];
}
最长上升子序列
题目描述
这是一个简单的动规板子题。
给出一个由 n ( n ≤ 5000 ) n(n\le 5000) n(n≤5000) 个不超过 1 0 6 10^6 106 的正整数组成的序列。请输出这个序列的最长上升子序列的长度。
最长上升子序列是指,从原序列中按顺序取出一些数字排在一起,这些数字是逐渐增大的。
输入格式
第一行,一个整数 n n n,表示序列长度。
第二行有 n n n 个整数,表示这个序列。
输出格式
一个整数表示答案。
样例 #1
样例输入 #1
6
1 2 4 1 3 4
样例输出 #1
4
提示
分别取出 1 1 1、 2 2 2、 3 3 3、 4 4 4 即可。
3.思路
暴力搜索,从第一个开始向前寻找比其小的数,如果比前面的大ans[i]=max(ans[i],1+ans[j]),最后找到ans[]当中最大的数。
4.代码
#include<bits/stdc++.h>
using namespace std;
int n;
int a[5001],ans[5001];
int main(){
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=1;i<=n;i++)
ans[i]=1;
for(int i=1;i<=n;i++){
for(int j=i-1;j>=1;j--){
if(a[i]>a[j]){
ans[i]=max(ans[i],1+ans[j]);
}
}
}
int mx=1;
for(int i=1;i<=n;i++)
mx=max(mx,ans[i]);
cout<<mx;
}
最大子段和
题目描述
给出一个长度为 n n n 的序列 a a a,选出其中连续且非空的一段使得这段和最大。
输入格式
第一行是一个整数,表示序列的长度 n n n。
第二行有 n n n 个整数,第 i i i 个整数表示序列的第 i i i 个数字 a i a_i ai。
输出格式
输出一行一个整数表示答案。
样例 #1
样例输入 #1
7
2 -4 3 -1 2 -4 3
样例输出 #1
4
提示
样例 1 解释
选取 [ 3 , 5 ] [3, 5] [3,5] 子段 { 3 , − 1 , 2 } \{3, -1, 2\} {3,−1,2},其和为 4 4 4。
数据规模与约定
- 对于 40 % 40\% 40% 的数据,保证 n ≤ 2 × 1 0 3 n \leq 2 \times 10^3 n≤2×103。
- 对于 100 % 100\% 100% 的数据,保证 1 ≤ n ≤ 2 × 1 0 5 1 \leq n \leq 2 \times 10^5 1≤n≤2×105, − 1 0 4 ≤ a i ≤ 1 0 4 -10^4 \leq a_i \leq 10^4 −104≤ai≤104。
3.思路
如果这个数加上前面的数和比自己小,则这个ans[i]=这个数,比自己大则ans[i]=ans[i-1]+a[i];最后再找出他们当中的最大和。
4.代码
#include<bits/stdc++.h>
using namespace std;
int n,mx;
int a[500001];
int ans[500001];
int main(){
cin>>n;
mx=INT_MIN;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=1;i<=n;i++){
ans[i]=max(a[i],ans[i-1]+a[i]);
mx=max(mx,ans[i]);
}
cout<<mx;
}
LCS
题面翻译
题目描述:
给定一个字符串 s s s 和一个字符串 t t t ,输出 s s s 和 t t t 的最长公共子序列。
输入格式:
两行,第一行输入 s s s ,第二行输入 t t t 。
输出格式:
输出 s s s 和 t t t 的最长公共子序列。如果有多种答案,输出任何一个都可以。
说明/提示:
数据保证 s s s 和 t t t 仅含英文小写字母,并且 s s s 和 t t t 的长度小于等于3000。
题目描述
文字列 $ s $ および $ t $ が与えられます。 $ s $ の部分列かつ $ t $ の部分列であるような文字列のうち、最長のものをひとつ求めてください。
输入格式
入力は以下の形式で標準入力から与えられる。
$ s $ $ t $
输出格式
$ s $ の部分列かつ $ t $ の部分列であるような文字列のうち、最長のものをひとつ出力せよ。 答えが複数ある場合、どれを出力してもよい。
样例 #1
样例输入 #1
axyb
abyxb
样例输出 #1
axb
样例 #2
样例输入 #2
aa
xayaz
样例输出 #2
aa
样例 #3
样例输入 #3
a
z
样例输出 #3
样例 #4
样例输入 #4
abracadabra
avadakedavra
样例输出 #4
aaadara
提示
注釈
文字列 $ x $ の部分列とは、$ x $ から $ 0 $ 個以上の文字を取り除いた後、残りの文字を元の順序で連結して得られる文字列のことです。
制約
- $ s $ および $ t $ は英小文字からなる文字列である。
- $ 1\ \leq\ |s|,\ |t|\ \leq\ 3000 $
Sample Explanation 1
答えは axb
または ayb
です。 どちらを出力しても正解となります。
Sample Explanation 3
答えは `` (空文字列) です。
4.代码
#include<bits/stdc++.h>
using namespace std;
int dp[5000][5000];
char a[10001],b[10001],ans[10001];
int main(){
int i,j;
scanf("%s%s",a+1,b+1);
for(i=1;i<=strlen(a+1);i++){
for(j=1;j<=strlen(b+1);j++){
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
if(a[i]==b[j])
dp[i][j]=max(dp[i][j],dp[i-1][j-1]+1);
}
}
i--,j--;
while(dp[i][j]>0){
if(a[i]==b[j]){
ans[dp[i][j]]=a[i];
i--, j--;
}
else{
if(dp[i][j]==dp[i-1][j]) i--;
else j--;
}
}
cout<<ans+1;
return 0;
}