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
答えは `` (空文字列) です。
思路:定义一个二维数组,每一个维度对应两个字符串的不同单词然后进行匹配最后得到结果如果能匹配则进行标记并判断是从x这个维度递推过来的还是从y这个维度或是直接就是xy各移动一格后的结果,直到最后,到最后判断直接递归遍历所有的可能来判断打印的字母
代码
#include<bits/stdc++.h>
using namespace std;
int n,m,i,j,dp[3010][3010];char a[3010],b[3010];bool x[3010][3010],y[3010][3010];
void print(int i,int j){
if(i==0||j==0) return ;
print(i-x[i][j],j-y[i][j]);//继续往前找
if(a[i]==b[j]) cout<<a[i];//如果相等,说明这个字符属于LCS
}
signed main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>a+1>>b+1;
n=strlen(a+1);m=strlen(b+1);
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
if(a[i]==b[j]) dp[i][j]=dp[i-1][j-1]+1,x[i][j]=1,y[i][j]=1;//dp数组是用来判断x,y数组的关系
else if(dp[i-1][j]>dp[i][j-1]) dp[i][j]=dp[i-1][j],x[i][j]=1;
else dp[i][j]=dp[i][j-1],y[i][j]=1;//LCS
print(n,m);//以栈的形式输出(逆向输出)
return 0;
}
[NOIP1996 提高组] 挖地雷
题目描述
在一个地图上有 N ( N ≤ 20 ) N\ (N \le 20) N (N≤20) 个地窖,每个地窖中埋有一定数量的地雷。同时,给出地窖之间的连接路径。当地窖及其连接的数据给出之后,某人可以从任一处开始挖地雷,然后可以沿着指出的连接往下挖(仅能选择一条路径),当无连接时挖地雷工作结束。设计一个挖地雷的方案,使某人能挖到最多的地雷。
输入格式
有若干行。
第 1 1 1 行只有一个数字,表示地窖的个数 N N N。
第 2 2 2 行有 N N N 个数,分别表示每个地窖中的地雷个数。
第 3 3 3 行至第 N + 1 N+1 N+1 行表示地窖之间的连接情况:
第 3 3 3 行有 n − 1 n-1 n−1 个数( 0 0 0 或 1 1 1),表示第一个地窖至第 2 2 2 个、第 3 3 3 个 … \dots … 第 n n n 个地窖有否路径连接。如第 3 3 3 行为 11000 ⋯ 0 11000\cdots 0 11000⋯0,则表示第 1 1 1 个地窖至第 2 2 2 个地窖有路径,至第 3 3 3 个地窖有路径,至第 4 4 4 个地窖、第 5 5 5 个 … \dots … 第 n n n 个地窖没有路径。
第 4 4 4 行有 n − 2 n-2 n−2 个数,表示第二个地窖至第 3 3 3 个、第 4 4 4 个 … \dots … 第 n n n 个地窖有否路径连接。
……
第 n + 1 n+1 n+1 行有 1 1 1 个数,表示第 n − 1 n-1 n−1 个地窖至第 n n n 个地窖有否路径连接。(为 0 0 0 表示没有路径,为 1 1 1 表示有路径)。
输出格式
第一行表示挖得最多地雷时的挖地雷的顺序,各地窖序号间以一个空格分隔,不得有多余的空格。
第二行只有一个数,表示能挖到的最多地雷数。
样例 #1
样例输入 #1
5
10 8 4 7 6
1 1 1 0
0 0 0
1 1
1
样例输出 #1
1 3 4 5
27
提示
【题目来源】
NOIP 1996 提高组第三题
思路:观察题目数据发现给的数据量很小于是我们可以考虑dfs直接遍历找最大值
代码
#include<iostream>
using namespace std;
//下面分别是联通情况,地雷数,记录答案路径,记录当前路径
int g[25][25], mind[25], ans[25], path[25];
bool visited[25];//记录是否走过
//下面分别为路径个数,记录走了多少点 ,记录最大值
int n, cnt, maxm;
bool check(int k)
{
for(int i = 1; i <= n; i++)
{
if(g[k][i] && !visited[i]) return false;
}
return true;
}
void dfs(int x, int step, int sum)//x现在的位置,step走过的点,sum记录现在挖到的地雷数
{
if(check(x))
{
if(maxm < sum)
{
maxm = sum;
cnt = step;
for(int i = 1; i <= step; i++)
{
ans[i] = path[i];
}
}
return;
}
for(int i = 1; i <= n; i ++)
{
if(g[x][i] && !visited[i])
{
visited[i] = 1;
path[step + 1] = i;
dfs(i, step + 1, sum + mind[i]);
visited[i] = 0;//
}
}
}
int main()
{
cin >> n;
for(int i = 1; i <= n; i++)
{
cin >> mind[i];
}
for(int i = 1; i <= n; i++)
for(int j = i + 1; j <= n; j++)
{
cin >> g[i][j];//单边图,幽默题目不说清
}
for(int i = 1; i <= n; i++)
{
visited[i] = 1;
path[1] = i;
dfs(i, 1, mind[i]);
visited[i] = 0;
}
for(int i = 1; i <= cnt; i++)
cout << ans[i] << ' ';
cout << endl << maxm;
return 0;
}
[NOIP2010 提高组] 乌龟棋
题目背景
NOIP2010 提高组 T2
题目描述
小明过生日的时候,爸爸送给他一副乌龟棋当作礼物。
乌龟棋的棋盘是一行 N N N 个格子,每个格子上一个分数(非负整数)。棋盘第 1 1 1 格是唯一的起点,第 N N N 格是终点,游戏要求玩家控制一个乌龟棋子从起点出发走到终点。
乌龟棋中 M M M 张爬行卡片,分成 4 4 4 种不同的类型( M M M 张卡片中不一定包含所有 4 4 4 种类型的卡片,见样例),每种类型的卡片上分别标有 1 , 2 , 3 , 4 1,2,3,4 1,2,3,4 四个数字之一,表示使用这种卡片后,乌龟棋子将向前爬行相应的格子数。游戏中,玩家每次需要从所有的爬行卡片中选择一张之前没有使用过的爬行卡片,控制乌龟棋子前进相应的格子数,每张卡片只能使用一次。
游戏中,乌龟棋子自动获得起点格子的分数,并且在后续的爬行中每到达一个格子,就得到该格子相应的分数。玩家最终游戏得分就是乌龟棋子从起点到终点过程中到过的所有格子的分数总和。
很明显,用不同的爬行卡片使用顺序会使得最终游戏的得分不同,小明想要找到一种卡片使用顺序使得最终游戏得分最多。
现在,告诉你棋盘上每个格子的分数和所有的爬行卡片,你能告诉小明,他最多能得到多少分吗?
输入格式
每行中两个数之间用一个空格隔开。
第 1 1 1 行 2 2 2 个正整数 N , M N,M N,M,分别表示棋盘格子数和爬行卡片数。
第 2 2 2 行 N N N 个非负整数, a 1 , a 2 , … , a N a_1,a_2,…,a_N a1,a2,…,aN,其中 a i a_i ai 表示棋盘第 i i i 个格子上的分数。
第 3 3 3 行 M M M 个整数, b 1 , b 2 , … , b M b_1,b_2,…,b_M b1,b2,…,bM,表示 M M M 张爬行卡片上的数字。
输入数据保证到达终点时刚好用光 M M M 张爬行卡片。
输出格式
一个整数,表示小明最多能得到的分数。
样例 #1
样例输入 #1
9 5
6 10 14 2 8 8 18 5 17
1 3 1 2 1
样例输出 #1
73
提示
每个测试点 1s。
小明使用爬行卡片顺序为 1 , 1 , 3 , 1 , 2 1,1,3,1,2 1,1,3,1,2,得到的分数为 6 + 10 + 14 + 8 + 18 + 17 = 73 6+10+14+8+18+17=73 6+10+14+8+18+17=73。注意,由于起点是 1 1 1,所以自动获得第 1 1 1 格的分数 6 6 6。
对于 30 % 30\% 30% 的数据有 1 ≤ N ≤ 30 , 1 ≤ M ≤ 12 1≤N≤30,1≤M≤12 1≤N≤30,1≤M≤12。
对于 50 % 50\% 50% 的数据有 1 ≤ N ≤ 120 , 1 ≤ M ≤ 50 1≤N≤120,1≤M≤50 1≤N≤120,1≤M≤50,且 4 4 4 种爬行卡片,每种卡片的张数不会超过 20 20 20。
对于 100 % 100\% 100% 的数据有 1 ≤ N ≤ 350 , 1 ≤ M ≤ 120 1≤N≤350,1≤M≤120 1≤N≤350,1≤M≤120,且 4 4 4 种爬行卡片,每种卡片的张数不会超过 40 40 40; 0 ≤ a i ≤ 100 , 1 ≤ i ≤ N , 1 ≤ b i ≤ 4 , 1 ≤ i ≤ M 0≤a_i≤100,1≤i≤N,1≤b_i≤4,1≤i≤M 0≤ai≤100,1≤i≤N,1≤bi≤4,1≤i≤M。
思路:题目给了四种走的步数的卡片,于是我们可以考虑走不同卡片分别对应的步数,从终点开始倒推,每次都选最大的那个操作就行
代码
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=41;
int F[MAXN][MAXN][MAXN][MAXN],num[351],g[5],n,m,x;
int main()
{
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>num[i];
F[0][0][0][0]=num[1];
for(int i=1;i<=m;i++)
{
cin>>x;
g[x]++;
}
for(int a=0;a<=g[1];a++)
for(int b=0;b<=g[2];b++)
for(int c=0;c<=g[3];c++)
for(int d=0;d<=g[4];d++)
{
int r=1+a+b*2+c*3+d*4;
if(a!=0) F[a][b][c][d]=max(F[a][b][c][d],F[a-1][b][c][d]+num[r]);
if(b!=0) F[a][b][c][d]=max(F[a][b][c][d],F[a][b-1][c][d]+num[r]);
if(c!=0) F[a][b][c][d]=max(F[a][b][c][d],F[a][b][c-1][d]+num[r]);
if(d!=0) F[a][b][c][d]=max(F[a][b][c][d],F[a][b][c][d-1]+num[r]);
}
cout<<F[g[1]][g[2]][g[3]][g[4]];
return 0;
}