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;
}
还是留一张牌子的照片把
唉