题目不赘述,在pta教育超市中可以买到。https://pintia.cn/problem-sets/1446838676759703552/exam/problems/1446838732288094208
一.懂得都懂
用深度优先遍历先预处理,之后按照要求找答案即可。
#include<bits/stdc++.h>
using namespace std;
int n,k;
int a[60];
map<int,int> mp;
void dfs(int u,int num,int sum){
if(num==4){
mp[sum]=1;
return;
}
if(u==n+1)return;
//2.
dfs(u+1,num,sum);
//1.
dfs(u+1,num+1,sum+a[u]);
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>k;
for(int i=1;i<=n;i++)cin>>a[i];
dfs(1,0,0);
while(k--){
int m;
cin>>m;
bool flag=true;
for(int i=1;i<=m;i++){
int temp;
cin>>temp;
if(!mp[temp*4]){
flag=false;
//break;
}
}
if(flag)puts("Yes");
else puts("No");
}
return 0;
}
二.芬兰木棋
使用斜率存储每条直线上的点,按照题目要求遍历每一条直线上的点。(如果难以理解,可以分四个象限使用斜率存储射线)
#include<bits/stdc++.h>
using namespace std;
struct node{
int x,y,w;
};
unordered_map<double,vector<node> > mp;
bool cmp(node a,node b){
if(a.x!=b.x)
return a.x<b.x;
return a.y<b.y;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int n;
cin>>n;
int sum=0;
while(n--){
int x,y,z;
cin>>x>>y>>z;
sum+=z;//无论是全1(按照个数) 还是按照分数
mp[1.0*y/x].push_back({x,y,z});//需要double类型精确
}
int num=0;
for(auto i:mp){//每个方向
auto v=i.second;
sort(v.begin(),v.end(),cmp);
for(int j=0;j<v.size();j++){
if(j==0||v[j].w!=1||v[j-1].w!=1)
num++;//1相连可以算成一次投掷,其他都要单次投掷
}
}
cout<<sum<<' '<<num<<endl;
return 0;
}
三.打怪升级
这道题是最难的一题,题目很难理解,考察floyed多源找最短路确定s,以及dijkstra单源最短路处理双权值问题。本题用堆优化dijktra比较方便,可以省去一部分自己写的代码,但是需要注意greater小根堆需要重载>号,具体原因:https://www.cnblogs.com/jyssh/p/16770809.html
#include<bits/stdc++.h>
using namespace std;
//要求1是多源最短路floyed求出s
//要求2是用dijkstra求s单源的两个判断标准(用堆优化不用写两种判断)
//要求1用邻接矩阵,要求2用邻接表(用结构体+vector也能实现)
typedef long long ll;
typedef pair<int,int> pii;
const int N=1010;
const int inf=1e9+10;
//边为双向边
#define x first
#define y second
struct node{
int v,w1,w2;
bool operator>(const node &x)const{//本在前,x在后 。 小根堆要定义大于号 (大就调整,即内一定不好)
if(w1!=x.w1)return w1>x.w1;
return w2<x.w2;
}
};
vector<node> edge[N];//dijkstra用到邻接表
int dist1[N],dist2[N];//dijkstra用到
int pre[N];
bool st[N];
int n,m;
void print(int x){
if(pre[x]==-1)return ;
print(pre[x]);
cout<<"->"<<x;
}
void dijkstra(int s){
//1.初始化
priority_queue<node,vector<node>,greater<node>> heap;//找最小(优)
for(int i = 1; i <= n; i++)dist1[i] = inf,dist2[i]=0, pre[i] = -1;
dist1[s]=0,dist2[s]=0;
heap.push({s,dist1[s],dist2[s]});//把s包入S集合中
while(heap.size()){
//2.找优
int u=heap.top().v;heap.pop();
if(st[u])continue;
//3.标记取出
st[u]=true;
//4.更新
for(auto ite:edge[u]){
int v=ite.v;
if(dist1[v]>dist1[u]+ite.w1){
dist1[v]=dist1[u]+ite.w1;
dist2[v]=dist2[u]+ite.w2;
pre[v]=u;//最优的推出者
heap.push({v,dist1[v],dist2[v]});
}
else if(dist1[v]==dist1[u]+ite.w1&&dist2[v]<dist2[u]+ite.w2){
dist2[v]=dist2[u]+ite.w2;
pre[v]=u;//最优的推出者
heap.push({v,dist1[v],dist2[v]});
}
}
}
return;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int e[N][N];//floyed一个要求用到
memset(e,0x3f,sizeof(e));
cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v,w1,w2;
cin>>u>>v>>w1>>w2;
e[u][v]=e[v][u]=w1;
//双向
edge[v].push_back({u,w1,w2});
edge[u].push_back({v,w1,w2});
}
//floyed解决要求1
//e在上方已经初始化完成
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
e[i][j]=min(e[i][j],e[i][k]+e[k][j]);
}
}
}
int s=-1,mx=inf;
for(int i=1;i<=n;i++){
int temp=0;
for(int j=1;j<=n;j++)temp=max(temp,e[i][j]);
if(temp<mx){
mx=temp;
s=i;
}
}
//dijkstra解决要求2(有两个条件)
dijkstra(s);
cout<<s<<endl;
int T;cin>>T;
while(T--){
int x;
cin>>x;
cout<<s;
print(x);//用递归函数(堆栈)实现倒序转化成正序输出
cout<<endl;
cout<<dist1[x]<<' '<<dist2[x]<<endl;
}
return 0;
}
四.疫情防控(22分/33分)
图像的连通性问题需要考虑并查集,然而并查集比较方便不断扩大插入,对于删除却不大擅长,并且实现的时间复杂度及其大,于是我们采用离线方法,先把所有输入存起来,再从最后一天的并查集基础上,不断加入新复活的点。
本题只能得到22分,看了下别人的代码,答题思路都一样,可能是因为邻接表和多个表的存储采用了vector存储,不如数组存储的访问时间快。但是vector存储最大优点是方便简单,如果用数组存储邻接表比较难以理解,详见https://www.acwing.com/activity/content/11/
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
#define x first
#define y second
//涉及到图像连通性的问题,一般都是用并查集(本题中正向不好删除,采用反向方法不断扩大并查集),这要求离线操作(先存起来)
//邻接表可以用数组方法,也可以用vector的方法比较方便
const int N=50010;
int n,m,d;
vector<int>g[N];
vector<pii>dele;//删除的记录cq
vector<pii>query[1100];//记录每一组询问
int f[N];//并查集
bool st[N];//判断最一开始剩下那些点
int find(int x){
if(f[x]==x)return x;
return find(f[x]);//find可以找到最顶端
}
void merge(int x,int y){
f[find(x)]=f[find(y)];//祖宗赋值到另一祖宗上
return ;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m>>d;
int temp1,temp2;
//读入线路
for(int i=1;i<=m;i++){
cin>>temp1>>temp2;
g[temp1].push_back(temp2);
g[temp2].push_back(temp1);//双向边
}
//读入新增和询问
for(int i=0;i<d;i++){
cin>>temp1>>temp2;
st[temp1]=true;
dele.push_back({temp1,temp2});
while(temp2--){
int x1,x2;
cin>>x1>>x2;
query[i].push_back({x1,x2});
}
}
//并查集初始化
for(int i=1;i<=n;i++)f[i]=i;
for(int i=1;i<=n;i++)//遍历n个点(邻接表存储)
{
if(!st[i]){
for(int j=0;j<g[i].size();j++)
if(!st[g[i][j]])merge(i,g[i][j]);
}
}
//依次增加并且扩大并查集(并查集只能增)
int ans[1100];
memset(ans,0,sizeof(ans));
for(int i=d-1;i>=0;i--){
int c=dele[i].x;
int q=dele[i].y;
for(int j=0;j<q;j++){
int fr=query[i][j].x;
int to=query[i][j].y;
if(find(fr)!=find(to))ans[i]++;//祖先不同,不连通
}
//恢复该点
st[c]=false;
for(int j=0;j<g[c].size();j++){
if(!st[g[c][j]])merge(c,g[c][j]);
}
}
for(int i=0;i<d;i++)cout<<ans[i]<<endl;
return 0;
}