目录
题目链接:PTA | 程序设计类实验辅助教学平台 (pintia.cn)
7-1 懂得都懂
输入样例:
5 3
4 8 12 20 40
3 11 16 19
3 12 16 19
10 11 11 11 11 11 11 11 11 11 11
输出样例:
Yes
No
Yes
具体代码实现:
//思路:每个数据都可以由原图任意4个不同数据的平均值计算而来
//1. 如果我们用一个数组来存储4个数据的和->数组大小为50*49*48*47(int足够)
//但是在查询的时候,需要每次进行一个遍历(高达200*200) ->不使用
//2. 原图四个值的和最大为 255*4,如果该值存在则标记为1,
//在查询值的时候只需查询该值是否为1即可。
#include<bits/stdc++.h>
using namespace std;
int f[1100],a[60],news[210]; //记录原图四个数据和、原图特征数据、新图特征数据
int m;
bool judge(){
for(int i=0;i<m;i++){
int t=news[i]*4;
if(!f[t])
return false;
}
return true;
}
int main(void){
int n,k; //原图的特征数据个数、新图的张数
cin>>n>>k;
for(int i=0;i<n;i++){
cin>>a[i]; //a存储原图特征数据
}
//1.记录原图任意四个数据的和(不用平均导致小数)
for(int i=0;i<n;i++){
for(int j=i+1;j<n;j++){
for(int k=j+1;k<n;k++){
for(int l=k+1;l<n;l++){
int t=a[i]+a[j]+a[k]+a[l];
f[t]=1;
}
}
}
}
for(int i=0;i<k;i++){
cin>>m;
for(int j=0;j<m;j++){
cin>>news[j];
}
if(i!=0)
cout<<endl;
if(judge())
cout<<"Yes";
else
cout<<"No";
}
}
7-2 芬兰木棋
输入样例:
11
1 2 2
2 4 3
3 6 4
-1 2 2
-2 4 3
-3 6 4
-1 -2 1
-2 -4 1
-3 -6 1
-4 -8 2
2 -1 999
输出样例:
1022 9
具体实现代码:
//思路:贪心
//1. 怎么获得的分数最多,次数最少
//木棋分数>1,+木棋上的分数(一次一根)
//木棋分数<=1,判断连续<=1的个数,一次击倒
//显而易见:最多分为木棋上的值的和
//2. 怎么确认方向
//1)最开始我想的是k,但是在k相等的条件下有两个方向—>不使用
//2)找到x,y两个位置的最大公约数(正),除以公约数之后相同则表示同一方向(因为正负不会发生改变)
//3. 怎么在同一方向下距离由小到大排序
//存储距离--》x平方+y平方,还需要存储木棋值
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
map<pair<ll,ll>,vector<pair<ll,ll> > >mp;
int main(void){
int n;
ll x,y,p;
cin>>n;
ll smax=0,nmax=0; //最大得分、最少次数
for(int i=0;i<n;i++){
cin>>x>>y>>p;
int t=abs(__gcd(x,y)); //得到最大公约数
mp[{x/t,y/t}].push_back({x*x+y*y,p});
smax+=p;
}
for(auto it:mp){ //使用for循环,则访问用.
vector<pair<ll,ll> >t=it.second;
sort(t.begin(),t.end());
for(int i=0;i<t.size();i++){
if(t[i].second!=1)
nmax++;
else{
if(t[i+1].second!=1||i+1==t.size())
nmax++;
}
}
}
cout<<smax<<" "<<nmax;
return 0;
}
tips:
遍历
map
:可以使用迭代器或者范围for
循环。
迭代器:
for (auto it = map.begin(); it != map.end(); ++it)
范围
for
循环:for (const auto& pair : map)
访问
map
的元素:
使用迭代器:
it->first
,it->second
使用范围
for
循环:pair.first
,pair.second
访问
pair
的元素:pair.first
,pair.second
7-3 打怪升级
输入样例:
6 12
1 2 10 5
2 3 16 20
3 1 4 2
2 4 20 22
4 5 2 2
5 3 12 6
4 6 8 5
6 5 10 5
6 1 20 25
1 5 8 5
2 5 2 1
2 6 8 5
4
2 3 6 5
输出样例:
5
5->2
2 1
5->1->3
12 7
5->4->6
10 7
5
0 0
具体实现代码:
1.采用dijkstra实现
//思路:采用迪杰斯特拉算法
//1.首先进行n次迪杰斯特拉算法得到空降堡垒编号,也就是需要我们存储每一个的最大值
//2. 求path,使用while即可
#include<bits/stdc++.h>
#define maxInt 0x3f3f3f
#define N 1010
using namespace std;
int w[N][N],v[N][N]; //能量、价值
int path[N],d[N],f[N]; //前驱点、最小能量,最大价值
bool vis[N]; //是否被访问过
int n,m,k;
vector<int> p; //存储具体路径
int dijkstra(int s){
memset(d,maxInt,sizeof(d));
memset(f,0,sizeof(f));
memset(path,-1,sizeof(path));
memset(vis,0,sizeof(vis));
d[s]=0;
f[s]=0;
for(int k=1;k<=n;k++){ //n次选点
//1. 寻找未被访问的点距离最小的
int t=-1; //最小能量下标
for(int i=1;i<=n;i++){
if(!vis[i]&&(t==-1||d[t]>d[i]))
t=i;
}
vis[t]=true;
//2.更新
for(int i=1;i<=n;i++){
if(d[i]>d[t]+w[t][i]){
d[i]=d[t]+w[t][i];
f[i]=f[t]+v[t][i];
path[i]=t;
}else if(d[i]==d[t]+w[t][i]){
//选择价值最大的
if(f[i]<f[t]+v[t][i]){
f[i]=f[t]+v[t][i];
path[i]=t;
}
}
}
}
//3. 存储最大d[]值
int ans=0;
for(int i=1;i<=n;i++){
if(ans<d[i])
ans=d[i];
}
return ans;
}
void findPath(int s,int d){
p.clear();
while(d!=s){ //倒叙存储,并且不包含源点
p.push_back(d);
d=path[d];
}
p.push_back(s);
reverse(p.begin(),p.end());
}
int main(void){
cin>>n>>m;
//初始化
memset(w,maxInt,sizeof(w));
memset(v,maxInt,sizeof(v));
memset(v,maxInt,sizeof(f));
memset(d,maxInt,sizeof(d));
memset(path,-1,sizeof(path));
for(int i=0;i<m;i++){
int b1,b2,c1,c2;
cin>>b1>>b2>>c1>>c2;
w[b1][b2]=w[b2][b1]=c1;
v[b1][b2]=v[b2][b1]=c2;
}
for(int i=1;i<n;i++){
v[i][i]=w[i][i]=0; //自己到自己等于0
}
int x=1;
int ans=dijkstra(1);
for(int i=2;i<=n;i++){
if(ans>dijkstra(i)){
ans=dijkstra(i);
x=i;
}
}
cout<<x<<endl; //得到最佳空降位置
dijkstra(x);
cin>>k;
for(int i=0;i<k;i++){
int t;
cin>>t;
findPath(x,t);
for(int j=0;j<p.size();j++){
if(j>0)
cout<<"->";
cout<<p[j];
}
cout<<endl;
cout<<d[t]<<" "<<f[t]<<endl;
}
return 0;
}
2. 采用floyed实现
//思路:只需要一次floyed即可
//1. 求合适空降点:对每一行w,求最大的值最小的行
//2.求路径:path递归求解
#include<bits/stdc++.h>
#define maxInt 0x3f3f3f3f
#define N 1010
using namespace std;
int n,m,k;
int w[N][N],v[N][N],path[N][N]; //能量、价值
void floyed(){
for(int k=1;k<=n;k++){ //k为中转点
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(w[i][j]>w[i][k]+w[k][j]){
w[i][j]=w[i][k]+w[k][j];
v[i][j]=v[i][k]+v[k][j];
path[i][j]=k;
}else if(w[i][j]==w[i][k]+w[k][j]){
//找总价值最高的
if(v[i][j]<v[i][k]+v[k][j]){
v[i][j]=v[i][k]+v[k][j];
path[i][j]=k;
}
}
}
}
}
}
//寻找具体路径
void findPath(int st,int end){
if(st==end){
return;
}
if(path[st][end]==(-1)){ //不用判断w[][],因为路径肯定存在
cout<<"->"<<end;
}else{
int mid=path[st][end];
findPath(st,mid);
findPath(mid,end);
}
}
int main(void){
cin>>n>>m;
//初始化
memset(w,maxInt,sizeof(w));
memset(v,0,sizeof(v));
memset(path,-1,sizeof(path));
for(int i=1;i<N;i++){
w[i][i]=0;
v[i][i]=0;
}
for(int i=0;i<m;i++){
int b1,b2,c1,c2;
cin>>b1>>b2>>c1>>c2;
w[b1][b2]=w[b2][b1]=c1;
v[b1][b2]=v[b2][b1]=c2;
}
floyed();
//找空降位置:数值最大的最小行
int x=0; //目标行(空降位置)
int ans=maxInt; //目标行的最大值
for(int i=1;i<=n;i++){ //n行进行计算
int t=0;
for(int j=1;j<=n;j++){ //找到第i行的w的最大值
if(w[i][j]>t)
t=w[i][j];
}
if(ans>t){
x=i;
ans=t;
}
}
cout<<x<<endl;
cin>>k;
for(int i=0;i<k;i++){
int d;
cin>>d;
cout<<x;
findPath(x,d);
cout<<endl;
cout<<w[x][d]<<" "<<v[x][d]<<endl;
}
return 0;
}
7-4 疫情防控
输入样例:
5 5 3
1 2
1 3
1 5
2 5
3 4
4 3
1 3
1 4
2 3
5 3
3 4
2 3
3 5
1 3
2 3
2 5
3 4
输出样例:
1
2
3
具体代码实现:
//题意:n个顶点m条边的无向图,每天都有一个点不可用,问是否可达
//->首先我想到的是每天遍历将其长度设为无穷,然后dfs()
//->每一次判断都要dfs+遍历vis数组;然后去点,时间复杂度太高
//于是使用并查集->加点
//思路:逆序每次加点,故需要存储每次的值
#include<bits/stdc++.h>
using namespace std;
const int N=50010;
vector<int> f[N]; //先存储结构,因为需要逆序
int fa[N]; //存储父节点
bool vis[N]; //true表示无法访问
int n,m,d; //机场数、航线数、新增防控地区的天数
int dd[N]; //表示第i天变成防空地区的编号
int ans[N]; //存储每天的结果
map<int,vector<pair<int,int> > > mp; //存储每天要访问的航线
void init(){
for(int i=1;i<=n;i++)
fa[i]=i;
}
int find(int i){
// if(fa[i]==i)
// return i;
// else{
// fa[i]=find(fa[i]);
// return fa[i];
// }
if(i!=fa[i])
fa[i]=find(fa[i]);
return fa[i];
}
void join(int i,int j){
int i_fa=find(i);
int j_fa=find(j);
if(i_fa!=j_fa)
fa[i_fa]=j_fa;
}
int main(void){
cin>>n>>m>>d;
for(int i=0;i<m;i++){
int a,b;
cin>>a>>b;
f[a].push_back(b);
f[b].push_back(a);
}
for(int i=1;i<=d;i++){ //d块输入
int c,q;
cin>>c>>q;
dd[i]=c;
vis[c]=true; //true表示成为防控地区
for(int j=1;j<=q;j++){ //q段行程
int x,y;
cin>>x>>y;
mp[i].push_back({x,y});
}
}
//逆序使用并查集
//1.初始化父节点
init();
//2. 构建最后一天的连通图
for(int i=1;i<=n;i++){
for(auto x:f[i]){
if(!vis[x]&&!vis[i]){ //两点均不是疫情点
join(i,x);
}
}
}
//3.逆向处理每天的起飞请求
for(int i=d;i>0;i--){
int res=0;
vector<pair<int,int> >v=mp[i];
for(int j=0;j<v.size();j++){
int x=v[j].first;
int y=v[j].second;
if(find(x)!=find(y))
res++;
}
ans[i]=res;
//4. 恢复前一天的疫情点
int t=dd[i];
vis[t]=false;
for(auto x:f[t]){
if(!vis[x]){
join(t,x);
}
}
}
for(int i=1;i<=d;i++){
if(i!=1) cout<<endl;
cout<<ans[i];
}
return 0;
}
代码自己写的,如果有啥错误欢迎指正!!!