HFOI2017.7.14校内赛(普及组)题解

时间:2021-04-27 19:18:41
1:全在其中
描述
你设计了一个新的加密技术,可以用一种聪明的方式在一个字符串的字符间插入随机的字符串从而对信息进行编码。由于专利问题,我们将不会详细讨论如何在原有信息中产生和插入字符串。不过,为了验证你的方法,有必要写一个程序来验证原来的信息是否全在最后的字符串之中。
给定两个字符串s和t,你需要判断s是否是t的“子列”。也就是说,如果你去掉t中的某些字符,剩下字符将连接而成为s。
输入
输入包括多个测试样例。每一个都是由空格分隔的由字母数字ASCII字符组成的两个特定的字符串s和t。s和t的长度不超过100000。
输出
对于每个测试样例,如果s是t的“子列”,则输出”Yes”,否则输出”No”
样例输入
sequence subsequence
person compression
VERDI vivaVittorioEmanueleReDiItalia
caseDoesMatter CaseDoesMatter
样例输出
Yes
No
Yes
No
很简单的一道裸题。将被判断是否包含子串的字符串遍历一遍,如果和子串中指针当前指向元素和目前字符串的元素相同,则将子串中的指针右移一位。
代码

#include<cstdio>
const int maxn=100005;
bool query(char *s1,char *s2)
{
int i=0,j=0,start=0,nc=0,state=0;
for(i=0;s1[i]!=0;i++)
{
state=0;
for(j=start;s2[j]!=0;j++)
if(s1[i]==s2[j])
{
start=j+1;
nc++;
state=1;
break;
}
if(state)continue;
}
if(nc==i)return 1;
else return 0;
}
int main()
{
char s1[maxn],s2[maxn];
while(~scanf("%s%s",s1,s2))printf("%s\n",query(s1,s2)?"Yes":"No");
return 0;
}
2.最大正方形
题目描述
在一个n*m的只包含0和1的矩阵里找出一个不包含0的最大正方形,输出边长。
输入格式:
输入文件第一行为两个整数n,m(1<=n,m<=100),接下来n行,每行m个数字,用空格隔开,0或1.
输出格式:
一个整数,最大正方形的边长
输入样例:
4 4
0 1 1 1
1 1 1 0
0 1 1 0
1 1 0 1
输出样例:
2
有一种时间复杂度为O(N^3),空间复杂度为O(N^2)的前缀和算法,在这里我就不做详细介绍了。
动态规划方法:记f(i,j)为正方形中右下角坐标为(i,j)的最大正方形,则状态转移方程为f(i,j)=min{f(i-1,j),f(i,j-1),f(i-1,j-1)}+1
边界条件:若a(i,j)为0时,f(i,j)=0
状态转移方程的推导:第一个部分保证向上f(i,j)行均为1,第二部分保证向左f(i,j)行均为1,第三部分保证中间部分为1,状态转移方程就是保证正方形中的元素全为1。
时间复杂度:O(N^2),空间复杂度:O(N^2),通过滚动数组法可以降至O(N)。
代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=105;
int n,m,a[maxn][maxn],f[maxn][maxn],maxx=-1;
int main()
{
memset(a,0,sizeof a);
memset(f,0,sizeof f);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)scanf("%d",&a[i][j]);
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)
{
if(a[i][j]==0)f[i][j]=0;
else f[i][j]=min(min(f[i-1][j-1],f[i-1][j]),f[i][j-1])+1;
}
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)maxx=max(maxx,f[i][j]);
printf("%d\n",maxx);
return 0;
}
3.最短路径问题Problem Description给你n个点,m条无向边,每条边都有长度d和花费p,给你起点s终点t,要求输出起点到终点的最短距离及其花费,如果最短距离有多条路线,则输出花费最少的。Input输入n,m,点的编号是1~n,然后是m行,每行4个数 a,b,d,p,表示a和b之间有一条边,且其长度为d,花费为p。最后一行是两个数 s,t;起点s,终点。n和m为0时输入结束。(1 Output输出 一行有两个数, 最短距离及其花费。Sample Input3 21 2 5 62 3 4 51 30 0Sample Output9 11根据题目就能知道,这题可以用Dijkstra来解决。无非是多开一个数组存储花费而已。代码:
#include<cstdio>
#include<cstring>
#define maxa 1005
#define INF 10000000
int n,m;
int dis[maxa];
bool mark[maxa];
int map[maxa][maxa],cost[maxa][maxa];
int mon[maxa];
void dijkstra(int s)
{
int i,j,k,Min,visa,p;
memset(mark,0,sizeof(mark));
for(i=1; i<=n; i++)
{
dis[i]=map[s][i];
mon[i]=cost[s][i];
}
dis[s]=0;
mon[s]=0;
mark[s]=1;
for(i=1; i<n; i++)
{
Min=INF;
for(j=1; j<=n; j++)
{
if(mark[j]) continue;
if(dis[j]<Min)
{
Min=dis[j];
k=j;
}
}
mark[k]=1;
for(p=1; p<=n; p++)
{
if(!mark[p] && map[k][p]!=INF)
{
visa=dis[k]+map[k][p];
if(visa<dis[p])
{
dis[p]=visa;
mon[p]=mon[k]+cost[k][p];
}
else if(visa==dis[p] && mon[k]+cost[k][p]<mon[p])
{
mon[p]=mon[k]+cost[k][p];
}
}
}
}
}
int main()
{
int i,j,a,b,c,d,from,to;
while(scanf("%d%d",&n,&m)!=EOF)
{
if(n==0&&m==0) break;
for(i=0; i<=n; i++)
{
for(j=0; j<=n; j++)
{
map[i][j]=INF;
cost[i][j]=INF;
}
}
for(i=0; i<m; i++)
{
scanf("%d%d%d%d",&a,&b,&c,&d);
if(c<map[a][b])
{
map[a][b]=map[b][a]=c;
cost[a][b]=cost[b][a]=d;
}
}
scanf("%d%d",&from,&to);
dijkstra(from);
printf("%d %d\n",dis[to],mon[to]);
}
return 0;
}