2015年题目收集来自这位博主的专栏【链接】
代码本身比博主的较为精简,更易初学者读懂。后期会更新其他几年题目
题目分类
- 病毒感染:模拟题
- 算式: DFS
- 航线交叉:区间DP
题目1:病毒感染
二刷简洁代码
直接打印了每个时间的病毒个数,为便于debug。
#include<cstdio>
#include<iostream>
#include<map>
#include<vector>
#include<set>
#include<algorithm>
using namespace std;
int n,m,t1,t2,k,cnt=0;
int a[50][50],vis[50][50];
int go[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
vector<int> atk;
void attack(int a1,int b1)
{
for(int i=0;i<4;i++)
{
int a2=go[i][0]+a1;
int b2=go[i][1]+b1;
if(a2>=0&&a2<n&&b2>=0&&b2<m)
//边界要求
if(a[a2][b2]==0&&vis[a2][b2]==1)
{
cnt++;
a[a2][b2]=1;
}
}
}
void defend(int a1,int b1)
{
vis[a1][b1]=0;
a[a1][b1]=0;
}
int main()
{
scanf("%d %d %d",&n,&m,&k);
fill(a[0],a[0]+2500,0);
fill(vis[0],vis[0]+2500,1);
for(int i=0;i<k;i++){
scanf("%d %d",&t1,&t2);
a[t1-1][t2-1]=1;
cnt++;
}
while(cnt!=0)
{
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
{
if(a[i][j]!=0)
{
a[i][j]++;
}
if(a[i][j]==4)
{
atk.push_back(i);
atk.push_back(j);
}
else if(a[i][j]==6)
{
cnt--;
defend(i,j);
}
}
//结束循环以后进行感染
for(int i=0;i<atk.size();i+=2)
attack(atk[i],atk[i+1]);
atk.clear();
printf("%d\n",cnt);
}
return 0;
}
一刷代码
需要注意当为4时,需要直接向外扩展;并且为防止遍历过程中,感染周围的人也被重复计算,我这里是通过给队列,在遍历之后,在进行感染操作。判断时为上一阶段的条件,即之前为3即可感染,之前为5即可治疗。(也可以考虑先状态++,再进行相应的判断)
#include<cstdio>
#include<iostream>
#include<string>
#include<cmath>
#include<algorithm>
#include<map>
#include<set>
#include<vector>
using namespace std;
int a[15][15],ka[15][15];
int go[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
int n,m,k,t1,t2,num=0;
int rungo(int i,int j)
{
int tmpn=0;
for(int z=0;z<4;z++)
{
t1=i+go[z][1];
t2=j+go[z][0];
if(t1>=1&&t1<=n&&t2>=1&&t2<=m&&a[t1][t2]==0&&ka[t1][t2]==0)
{
a[t1][t2]=1;
//printf("上下左右: %d %d\n",t1,t2);
// 如果往后找,后的值已经被计数过,所以需要返回传染值
// if(z%2==1)
tmpn++;
}
}
//printf("传染: %d\n",tmpn);
return tmpn;
}
int run()
{
int nmax=0;
while(num!=0)
{
num=0;
vector<int> gan;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
//病毒滋生
if(ka[i][j]==0)
{
if((a[i][j]>0&&a[i][j]<3)||a[i][j]==4)
{
//printf("对应的点:%d %d %d\n",i,j,a[i][j]);
num++;
a[i][j]++;
}
//病毒感染
else if(a[i][j]==3)
{
//printf("对应的点:%d %d %d\n",i,j,a[i][j]);
num++;
a[i][j]++;
gan.push_back(i);
gan.push_back(j);
}
//病毒治疗
else if(a[i][j]==5)
{
a[i][j]=0;
//抗体函数
ka[i][j]=1;
}
}
for(int i=0;i<gan.size();i+=2)
{
int gi=gan[i],gj=gan[i+1];
num+=rungo(gi,gj);
}
if(nmax<num)nmax=num;
printf("%d\n",num);
}
}
int main()
{
fill(a[0],a[0]+15*15,0);
fill(ka[0],ka[0]+15*15,0);
scanf("%d %d %d",&n,&m,&k);
for(int i=0;i<k;i++)
{
scanf("%d %d",&t1,&t2);
a[t1][t2]=1;
num++;
}
run();
return 0;
}
2015年 题目2:
题目2:找到算式
二刷代码
时间原因,省略了打印结果,但已存到res中
#include<cstdio>
#include<iostream>
#include<map>
#include<vector>
#include<set>
#include<algorithm>
using namespace std;
int n,num,a[50],cnt=9999999;
vector<int> qu,res;
//只把符号入队列
void dfs(int t)
{
if(t==n-1)
{
int tmp_cnt=a[0];
for(int i=0;i<qu.size();i++)
if(qu[i]==0) tmp_cnt+=a[i+1];
else tmp_cnt*=a[i+1];
//如果比预期值大于等于,并且较小
if(tmp_cnt>=num&&tmp_cnt<cnt)
{
res=qu;
cnt=tmp_cnt;
}
qu.pop_back();
return ;
}
qu.push_back(0);
dfs(t+1);
qu.push_back(1);
dfs(t+1);
qu.pop_back();
}
int main()
{
scanf("%d %d",&n,&num);
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
dfs(0);
printf("%d\n",cnt);
return 0;
}
//4 235
//34 12 5 5
题解:模拟题(题目1)是真的烦啊啊啊,这题想到用dfs的话,会非常简单,10几分钟就能得到答案。清华的机试感觉数据并不是非常大,PAT是真的坑,非要搞边界。唯一提醒的就是需要再递归的时候,将符号出栈,这样可以实现很好的递归效果。
#include<cstdio>
#include<iostream>
#include<string>
#include<cmath>
#include<algorithm>
#include<map>
#include<set>
#include<vector>
using namespace std;
int a[15],b[15];
vector<int> que,res;
int n,d,minv=999999;
void dfs(int i)
{
if(i==n+1)
{
if(b[i-1]>=d&&minv>b[i-1])
{
minv=b[i-1];
res=que;
}
return ;
}
b[i]=a[i]*b[i-1];
que.push_back(0);
dfs(i+1);
que.pop_back();
b[i]=a[i]+b[i-1];
que.push_back(1);
dfs(i+1);
que.pop_back();
}
int main()
{
scanf("%d %d",&n,&d);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
b[0]=1;
dfs(1);
if(minv!=d)
printf("%d\n",minv);
else
{
for(int i=1;i<res.size();i++)
{
printf("%d",a[i]);
if(res[i]==0) printf("*");
else printf("+");
}
printf("%d\n",a[n-1]);
}
return 0;
}
题目3:
题目3:航线交叉
难点在如何思考出为最长上升子序列:1.最直观的感觉就是在有序的排列航线点时要不能交叉。 2.于是,不交叉=当前面点的南点为i时,后面点的南点j,需要满足j>i(不可能等于,因为题目规定) 3.所以成了最长上升子序列
类似桶排序的做法,把北边固定的前提下,计算一个子序列中上升的个数。做法按最简单的n^2做的,后期看看优化。
状态
dp[i]表示以位置i结尾的最上升子序列长度
方程
dp[i]=1+max{ dp[j] } (j<i且a[j]<a[i])
二刷代码
#include<cstdio>
#include<iostream>
#include<map>
#include<vector>
#include<set>
#include<algorithm>
using namespace std;
int n,t2,t1,a[500],dp[500],maxv=-1;
int main()
{
scanf("%d",&n);
fill(a,a+500,0);
fill(dp,dp+500,1);
//dp初始化
for(int i=0;i<n;i++)
{
scanf("%d %d",&t1,&t2);
a[t1]=t2;
}
for(int i=1;i<=n;i++)
for(int j=1;j<i;j++)
{
if(a[i]>a[j])
dp[i]=max(dp[j]+1,dp[i]);
maxv=max(maxv,dp[i]);
}
printf("%d\n",n-maxv);
return 0;
}
//4 235
//34 12 5 5
#include<cstdio>
#include<iostream>
#include<string>
#include<cmath>
#include<algorithm>
#include<map>
#include<set>
#include<vector>
using namespace std;
vector<int> a,dp;
int n,d,t1,t2,minv=-1;
int main()
{
scanf("%d",&n);
a.resize(n+1);
dp.resize(n+1);
for(int i=1;i<=n;i++)
{
scanf("%d %d",&t1,&t2);
a[t1]=t2;
}
//初始化
fill(dp.begin(),dp.end(),0);
dp[1]=1;
//状态转移
for(int i=2;i<=n;i++)
{
for(int j=1;j<=i;j++)
{
//这一段表示,存在递增关系
if(a[i]>a[j])
dp[i]=max(dp[i],dp[j]);
}
//将存在递增里,取最长的序列
dp[i]=dp[i]+1;
if(minv<dp[i]) minv=dp[i];
}
printf("%d\n",n-minv);
return 0;
}