最长公共子序列
我们称序列Z = < z1, z2, ..., zk >是序列X = < x1, x2, ..., xm >的子序列当且仅当存在严格上升的序列< i1, i2, ..., ik >,使得对j = 1, 2, ... ,k, 有xij = zj。比如Z = < a, b, f, c > 是X = < a, b,c, f, b, c >的子序列。 现在给出两个序列X和Y,你的任务是找到X和Y的最大公共子序列,也就是说要找到一个最长的序列Z,使得Z既是X的子序列也是Y的子序列。
输入格式:
输入包括多组测试数据。每组数据包括一行,给出两个长度不超过200的字符串,表示两个序列。两个字符串之间由若干个空格隔开。
输出格式:
对每组输入数据,输出一行,给出两个序列的最大公共子序列的长度。
原理:a[i],b[j]为两个序列。
f[i][j]的值有四种可能
一、f[i-1][j-1] a[i]和b[j]均不是最长公共子序列中的字符
二、f[i-1][j] a[i]不是,b[j]是
如:abcd,babc 最长公共子序列明显是abc,这时f[4][4]的值其实就等于f[3][4]
三、f[i][j-1] 与二的情况相似
这三种情况就是a[i],b[j]还未比较时f[i][j]可以提前得到的初始值
注意第一种情况已经包含在了二三中,所以在代码中我们只需要比较二三种情况的最大值
四、
a[i]与b[j] 是相同的字符。
这时我们可以进行两种操作:把两个字符选入最长公共子序列中,不进行任何操作
不进行任何操作 f[i][j]依旧是f[i][j]
当选入后,f[i][j]=f[i-1][j-1]+1,因为第二种情况与第三种情况a[i]或b[j]都在最长公共子序列中,但是现在是将两个字符选入进最长公共子序列选进去,所以这两个字符比不可能在最长公共子序列中,而这个情况的最大值就是f[i-1][j-1],所以当选入后,最长公共子序列的长度会增加1,即f[i-1][j-1]+1;
所以 f[i][j]=max(f[i][j],f[i-1][j-1]+1)
代码:
#include<stdio.h>
#include<string.h>
#include<queue>
#include<algorithm>
#include<cstdio>
#include<iostream>
using namespace std;
const int N=1010;
char a[N],b[N];
int f[N][N];
int n,m;
int main()
{
while(cin>>a+1) //多组输入,cin>>a+1指从a[1]开始输入
{
cin>>b+1;
memset(f,0,sizeof f); //将f数组归零
n=strlen(a+1); //n为a字符串长度
m=strlen(b+1); //m为b字符串长度
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
f[i][j]=max(f[i-1][j],f[i][j-1]);
if(a[i]==b[j]) f[i][j]=max(f[i][j],f[i-1][j-1]+1);
}
printf("%d\n",f[n][m]);
}
}
最长上升子序列
一个数的序列bi,当b1 < b2 < ... < bS的时候,我们称这个序列是上升的。对于给定的一个序列(a1, a2, ..., aN),我们可以得到一些上升的子序列(ai1, ai2, ..., aiK),这里1 <= i1 < i2 < ... <iK <= N。比如,对于序列(1, 7, 3, 5, 9, 4, 8),有它的一些上升子序列,如(1, 7), (3, 4, 8)等等。这些子序列中最长的长度是4,比如子序列(1, 3, 5, 8)。
你的任务,就是对于给定的序列,求出最长上升子序列的长度。
输入格式:
输入有很多组,每组输入的第一行是序列的长度N (1 <= N <= 1000)。第二行给出序列中的N个整数,这些整数的取值范围都在0到10000。
输出格式:
输出每组的最长上升子序列的长度。
时间O(n^2)
f[i]表示以a[i]为结尾的最大上升子序列
如果在i之前有a[j]<a[i] 那么f[i]就可以变为max(f[i],f[j]+1)
注:初始f[i]都为1
#include<stdio.h>
#include<string.h>
#include<queue>
#include<algorithm>
#include<cstdio>
using namespace std;
const int N=1010;
int f[N],a[N];
int n;
int main()
{
while(cin>>n) //多组输入
{
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++)
{
f[i]=1; //初始化f[i],f[i]为以w[i]为结尾的最大上升子序列
for(int j=1;j<i;j++)
{
if(a[j]<a[i]) f[i]=max(f[i],f[j]+1);
}
}
int res=0;
for(int i=1;i<=n;i++) res=max(res,f[i]); //寻找最长的最大上升子序列
cout<<res<<endl; //输出
}
}
寻找M
题目描述:
给出一个整数n,编程求出一个非零整数m,使得m是n的倍数,并且m的十进制表示中只有1和0。给出的n不大于200并且肯定存在对应的m,m是十进制数并且不大于100位。
输入:
输入包含多组测试数据。每组测试数据只有一个整数n (1 <= n <= 200)。整数0标志输入的结束。
输出:
对于每个n输出对应的整数m,m的十进制表示不多于100位。如果对于一个n存在多个合法的m,你只需输出一个即可。
下面是只能在poj上过,不能在pta上过的代码
不要使用word文档的代码会错!!!!!
原因:输出的结果会很大导致爆int,所以试了一下用long long 队列去存,但发现超时,于是用dfs去求,当深度超过19时这个数已经过大就强制跳出这条路搜其他路
#include<stdio.h>
#include<string.h>
#include<queue>
#include<algorithm>
#include<cstdio>
using namespace std;
typedef long long ll;
int n;
bool flag;
void dfs(int x,ll y) //x为深度,y为所求答案
{
if(x>19||flag==true) return; //超过19层或者找到答案,跳出这条路
if(y%n==0) //如果y是n的倍数,输出答案
{
flag=true;
printf("%lld\n",y);
return;
}
dfs(x+1,y*10); //因为所要求的数只有1和0所以只有?????0和?????1两种状态,层数要+1
dfs(x+1,y*10+1);
}
int main()
{
while(scanf("%d",&n),n) //多组输入,n=0时结束
{
flag=false; //将判断归0
dfs(1,1); //从第一层开始搜,第一个数为1
}
}
pta能过,poj会卡的代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
void bfs(int n)
{
queue<int> q;
q.push(1);
while(q.size())
{
int t=q.front();
q.pop();
if(t%n==0)
{
printf("%d\n",t);
break;
}
q.push(t*10);
q.push(t*10+1);
}
}
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
if(n==0) break;
bfs(n);
}
}
这是两个都能过的代码
#include<stdio.h>
#include<string.h>
#include<queue>
#include<algorithm>
#include<cstdio>
#include<iostream>
#include<vector>
#include<bitset>
using namespace std;
typedef long long ll;
int main() {
int n;
while (true){
scanf("%d",&n);
if (n == 0) break;
for (int i = 1; ;i++) {
ll res=1;
ll j=i;
while(j!=1)
{
res=res*10;
if(j&1) res++;
j>>=1;
}
if (res % n == 0) {
cout << res << endl;
break;
}
}
}
}
移动桌子(Moving Tables)(1463)
(来源:POJ 1083,ZOJ 1029,ACM国际大学生程序设计竞赛题解(1) P282)
问题描述:
著名的ACM(Advanced Computer Maker)公司租用了一层有400个房间的办公楼,结构如下。
ROOM 1 | ROOM 3 | ROOM 5 | … | ROOM 397 | ROOM 399 |
corrdior | |||||
ROOM 2 | ROOM 4 | ROOM 6 | … | ROOM 398 | ROOM 400 |
这层楼沿着走廊南北向的两边各有200个房间。最近,公司要做一次装修,需要在各个办公室之间搬运办公桌。由于走廊狭窄,办公桌都很大,走廊里一次只能通过一张办公桌。必须制定计划提高搬运效率。经理制定如下计划:一张办公桌从一个房间移动到另一个房间最多用十分钟。当从房间i移动一张办公桌到房间j,两个办公室之间的走廊都会被占用。所以,每10分钟内,只要不是同一段走廊,都可以在房间之间移动办公桌。为了说得更清楚一些,经理举例说明哪些情况可以同时移动,哪些情况不能同时移动。
Table Moving | Reason | |
Possible | (room 30 to 50) and (room 60 to 90) | No part of corridor is shared |
(room 11 to 12) and (room 14 to 13) | No part of corridor is shared | |
Impossible | (room 20 to 40) and (room 31 to 80) | Corridor in front of room 31 to room 40 is shared |
(room 1 to 4) and (room 3 to 6) | Corridor in front of room 3 is shared | |
(room 2 to 8) and (room 7 to 10) | Corridor in front of room 7 is shared |
每个房间,只有一张办公桌进出。现在,经理想找到一种方案,使移动桌子的事情尽快完成。请编写程序解决经理的难题。
输入:
输入数据有T组测试例,在第一行给出测试例个数(T)。每个测试例的第一行是一个整数N(1≤N≤200),表示要搬运办公桌的次数。接下来N行,每行两个正整数s和t,表示一张桌子,是从房间号码s移到房间号码t。有多组输入数据,输入第一行为一个表示输入数据总数的整数N,然后是N组输入数据。
输出:
每组输入都有一行的输出数据,为一整数T,表示完成任务所花费的最少时间。
由题得上下两个房间所占用的走廊是相同的,所以可以变化i为(i-1)/2使得上下两个房间编号相同,即它们的走廊号相同,然后每个s和t就是s到t要占用一次,当所有桌子搬运完成后,遍历一次所有走廊号,它们被占用几次,就代表要单独安排几次,而最短时间,就是这些被占用次数中最大的值。
#include<stdio.h>
#include<string.h>
#include<queue>
#include<algorithm>
#include<cstdio>
#include<vector>
using namespace std;
const int N=300;
int cor[N]; //col[i]表示走廊号i被占用的次数
int n,t;
int main()
{
scanf("%d",&t); //多组输入
while(t--)
{
memset(cor,0,sizeof cor); //归零
scanf("%d",&n);
for(int i=0;i<n;i++)
{
int l,r;
scanf("%d%d",&l,&r);
if(l>r) swap(l,r); //因为输入中有l>r的情况,不容易循环,保证l<r
l=(l-1)/2; //将l变成l所代表的走廊号
r=(r-1)/2; //将r变成r所代表的走廊号
for(int j=l;j<=r;j++) cor[j]++; //从l到r,将此段的所有被占用的走廊号+1
}
int ans=0;
for(int i=0;i<210;i++) ans=max(ans,cor[i]); //将所有的走廊号的被占用次数遍历一遍,得到最大被占用数,即为最少需要的时间
printf("%d\n",ans*10); //不要忘了乘10
}
}