河南第十二届省赛 题解+反思

本文详细解析了河南第十二届省赛中的算法题目,包括DNA序列问题、地铁收入计算、缆车路径分析等,提供了KMP算法、动态规划、图论等多种解题思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2019/5/7 河南第十二届省赛,铜...

2019/6/5 终于有时间,有题目,有心思来总结了.......

不知道数据有没有被暗改过....将就着看把,希望明年出问题的是做题的,而不再是出题的!

没有和出题人心意相通,前期题都没做完,没时间开后期题。

A:DNA序列---复制问题  

题意:给字符串s和t,求t在s中出现几次,如果t不是回文串,将t反过来再求一次。

思路: 暴力匹配orKMP。

1.题面看懵,僵住好久.... 2.说好的s<300??????????????? 

我没有用KMP,上来第一WA是日常傻逼,第二WA不出问题应该是WA在305,小吴老师上来string写了一发KMP过了。(还算幸运)有人WA到结束...

#include<bits/stdc++.h>
using namespace std;
#define LL long LONG_MAX
const int MX = 1e6+5;
int nt[MX];char p[MX],t[MX],pp[MX];
void GetNext(char *p,int *net){
    int plen=strlen(p);
    net[0]=-1;int k=-1,j=0;
    while(j<plen){
        if(k==-1||p[j]==p[k]) ++k,++j,net[j]=k;
        else k=net[k];
    }
}
int Kmp(char *t,char *p,int *net){
    int i=0,j=0,re=0,lt=strlen(t),lp=strlen(p);
    while(i<lt){
        while(i<lt&&j<lp){
            if(j==-1||t[i]==p[j])i++,j++;
            else j=net[j];
        }
        if(j==lp)re++,j=net[j];
    }
    return re;
}
bool same(char *x,char *y){
    int len=strlen(x);
    for(int i=0;i<len;i++){
        if(x[i]!=y[i])return 0;
    }
    return 1;
}
int main(){
    int T;cin>>T;while(T--){
        scanf("%s%s",p,t);
        strcpy(pp,p);
        GetNext(p,nt);
        int ans=Kmp(t,p,nt);
        reverse(p,p+strlen(p));
        if(!same(pp,p))
            ans+=Kmp(t,p,nt);
        printf("%d\n",ans);
    }
    return 0;
}

B:DNA序列---同源问题

题意:将字符串s转化成t,如果某一位匹配得分a,替换s的某一位得分-b,增加s某一位得分-c,丢失s某一位得分-d,求最大的得分。

思路:明显的lcs变形,转移公式就直接看代码把。

题意又得领会好久.....这题zzq说他少考虑了先丢失再增加的情况,但是后期给的数据没有这种情况,也不知道怎么回事,唉...应该抽10分种讨论一下,说不定有生机。   下来写了一发就过(。。)

#include<bits/stdc++.h>
using namespace std;
#define LL long LONG_MAX
const int MX = 1e6+5;
int dp[305][305];char s[305],t[305];
int main(){
    int a,b,c,d;
    while(cin>>a>>b>>c>>d){
        //if(b>c+d)b=c+d;
        scanf("%s%s",s,t);
        int ls=strlen(s),lt=strlen(t);
        for(int i=0;i<=ls;i++){
            dp[i][0]=-d*i;
        }
        for(int j=0;j<=lt;j++){
            dp[0][j]=-c*j;
        }
        for(int i=1;i<=ls;i++){
            for(int j=1;j<=lt;j++){
                if(s[i-1]==t[j-1])dp[i][j]=dp[i-1][j-1]+a;
                else dp[i][j]=max(dp[i-1][j-1]-b,max(dp[i][j-1]-c,dp[i-1][j]-d));
            }
        }
        printf("%d\n",dp[ls][lt]);
    }
    return 0;
}

C:DNA序列---变异问题

题意:给n个密码子(互不为前缀),给一个原始字符串T(由密码子组成),给k个和T等长的字符串si,求出每个si有几个密码子是改变了的。

思路:直接把T拆开来,然后将每一段的起点终点push到vector,然后和si匹配就行。

我敲这题的时候还算顺利,一发就过了,心态稳了一下。 出题人说什么本意要建立哈夫曼树??

#include<bits/stdc++.h>
using namespace std;
#define LL long LONG_MAX
const int MX = 1e6+5;
string a[105],s[15],t;
vector<pair<int,int> >v;
int main(){
    int n,k;while(cin>>n>>k){
        v.clear();
        for(int i=1;i<=n;i++)cin>>a[i];
        cin>>t;
        for(int i=0;i<(int)t.length();){
            for(int j=1;j<=n;j++){
                int tag=1;
                for(int k=0;k<(int)a[j].length();k++){
                    if(i+k>=(int)t.length()){tag=0;break;}
                    if(t[i+k]!=a[j][k]){tag=0;break;}
                }
                if(tag){
                    v.push_back(make_pair(i,i+a[j].length()-1));
                    i+=a[j].length();break;
                }
            }
        }
        for(int i=1;i<=k;i++){
            cin>>s[i]; int ans=0;
            for(int j=0;j<(int)v.size();j++){
                for(int k=v[j].first;k<=v[j].second;k++){
                    if(s[i][k]!=t[k]) {ans++;break;}
                }
            }
            printf("%d\n",ans);
        }
    }
    return 0;
}
 

D:地铁1号线

题意:给n个时间点,对应n个收入,n个点连起来构成线性关系,就是一天的收入表,每m分种发一班车,求一天的收入。

思路:枚举每一列车,暴力求落在的区间

1.不知道发车的第一个时间点和最后一个时间点   2.不知道时间是否有序。我也不知道场上wa哪里了。。。本来是以为发车时间或者结束时间有问题,结果后台都是6:00和22:00,而且时间是有序的。。。挺难受,一水题。

#include<bits/stdc++.h>
using namespace std;
#define LL long LONG_MAX
const int MX = 1e6+5;
struct no{
    int shi,fen;double price;
    int p;
}a[200];
bool operator<(const no & x,const no & y){
    if(x.shi==y.shi)return x.fen<y.fen;
    return x.shi<y.shi;
}
int f(int s1,int f1,int s2,int f2){
    return (s2-s1)*60+f2-f1;
}
int main(){
    int T;cin>>T;while(T--){
        int m,n;cin>>m>>n;
        for(int i=1;i<=n;i++){
            scanf("%d:%d%lf",&a[i].shi,&a[i].fen,&a[i].price);
        }
        sort(a+1,a+1+n);
        for(int i=1;i<=n;i++) a[i].p=f(a[1].shi,a[1].fen,a[i].shi,a[i].fen);
        int now=0;double ans=0;
        while(now<=a[n].p){
            for(int i=1;i<n;i++){
                if(now>=a[i].p&&now<=a[i+1].p){
                    double x1=a[i].p,x2=a[i+1].p;
                    double y1=a[i].price,y2=a[i+1].price;
                    double add=y1+(now-x1)/(x2-x1)*(y2-y1);
                    ans+=add;
                    break;
                }
            }
            now+=m;
        }
        printf("%d\n",(int)ans);
    }
    return 0;
}
 

E:缆车

题意:给一棵树,求有多少条路径的结点编号,是先递增后递减的。

思路:先求出每一个节点作为根节点,满足向下递减的节点数,RT,绿色满足。

先将树拎起来,然后DFS后续遍历求出每一个节点能向下延申的节点数,(此时父节点未计算

在DFS先序遍历,求出每一个节点父节点能作为分支向下延申的节点数。

最后 枚举每一个节点作为路径的转折点即可。

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int MX = 2e5+5;
vector<int>g[MX];
int de[MX];
void dfs(int x,int last){
    for(auto it:g[x]){
        if(it==last)continue;
        dfs(it,x);
        if(it<x) de[x]+=1+de[it];
    }
}
void DFS(int x,int last){
    for(auto it:g[x]){
        if(it==last)continue;
        if(it>x) de[it]+=1+de[x];
        DFS(it,x);
    }
}
int main(){
    int T;cin>>T;while(T--){
        memset(de,0,sizeof(de));
        int n;cin>>n;for(int i=1;i<=n;i++)g[i].clear();
        for(int i=1;i<n;i++){
            int su,sv;scanf("%d%d",&su,&sv);
            g[su].push_back(sv);g[sv].push_back(su);
        }
        dfs(1,1);
        DFS(1,1);
        LL ans=0;
        for(int i=1;i<=n;i++){
            for(auto it:g[i]){
                if(it<i)
                    ans=ans+(LL)(de[i]-de[it]-1)*(LL)(de[it]+1);
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}
/*
8
8
7 3
7 5
3 1
3 2
5 6
5 4
1 8
*/

F:Information Transmission-1

题意:给一01矩阵,连着的两个1可以配对,求最多的配对数。

思路:标准二分匹配,复杂度O(VE) 数据得跑18s....

首先是poj原题的把?这个数据给的,nlogn是极限了,而且场上3题的队伍都有过的,我反正没去想二分图,优先队列,队列瞎搞了一发没过。场上一群乱搞过?????

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#include<climits>
using namespace std;
#define LL long long
const LL mod = 1000000007;
const int MX = 1e6+1;
char mp[505][505];
int go[4][2]={0,1,1,0,-1,0,0,-1};
int used[MX],match[MX],n,m,cnt;
vector<int>g[MX];
int toint(int x,int y){
    return x*m+y;
}
/*void topoint(int &x,int &y,int p){
    x=p/n,y=p-p/n*n;
}*/
bool HA(int v){  //dfs找增广路
    used[v]=1;
    for(int i=0;i<g[v].size();i++){
        int u=g[v][i],w=match[u];
        if(w<0||!used[w]&&HA(w)){
            match[u]=v;
            match[v]=u;
            return 1;
        }
    }
    return 0;
}
int bipartite_matching(){  //记得加入双向边
    int res=0;
    memset(match,-1,sizeof(match));
    for(int v=0;v<n*m;v++){    //直接按照时间点往后面找
        if(match[v]<0){
            memset(used,0,sizeof(used));
            if(HA(v))
                res++;
        }
    }
    return res;
}
void init(){
    for(int i=0;i<=n*m;i++)g[i].clear();
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            if(mp[i][j]=='1'){
                cnt++;
                for(int k=0;k<4;k++){
                    int th=i+go[k][0],tl=j+go[k][1];
                    if(th<0||tl<0||th>=n||tl>=m)continue;
                    if(mp[th][tl]=='0')continue;
                    int st=toint(i,j),ed=toint(th,tl);
                    g[st].push_back(ed);
                }
            }
        }
    }
}
int main(){
    int T;scanf("%d",&T);while(T--){
        scanf("%d%d",&n,&m);cnt=0;
        for(int i=0;i<n;i++)cin>>mp[i];
        init();
        int pairred=bipartite_matching();
        printf("%d\n",cnt-pairred);
    }
    return 0;
}
 

G:Information Transmission-2

tarjan强连通,似乎数据也有锅,但是能做把。。留。

H:Information Transmission-3

最短路改写,zzq spfa哪里出了一点小问题,留

J:Special Formation

题意:给一个中序遍历的二叉树,第i个遍历的节点标号为i,求一个根节点的子节点的最大值和最小值。

思路:求能整除2几次即可

这题无锅

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int MX = 1e6+5;
 
int main(){
    vector<unsigned long long>v;
    for(unsigned long long i=1;i<LLONG_MAX;i=i*2) v.push_back(i);
    int n;cin>>n;while(n--){
        LL x,xx;cin>>x;xx=x;LL cnt=0;
        while(xx%2==0) xx/=2,cnt++;
        cout<<x-v[cnt]+1<<' '<<x+v[cnt]-1<<endl;
    }
    return 0;
}

还是留一张牌子的照片把

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值