A-Hierarchy
最小生成树,在建树时要判断一下后面那个点有没有被管理过,加一个VIS记录即可
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e4+10;
struct node
{
int x,y,z;
}e[maxn];
int fa[maxn],a[maxn];
bool vis[maxn];
bool cnm(node a,node b)
{
return a.z<b.z;
}
int find(int x)
{
if(x==fa[x])return x;
return fa[x]=find(fa[x]);
}
int main()
{
int n,m;
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
cin>>m;
for(int i=1;i<=m;i++)
{
cin>>e[i].x>>e[i].y>>e[i].z;
}
sort(e+1,e+m+1,cnm);
int cnt=0,ans=0;
for(int i=0;i<=n;i++)fa[i]=i;
for(int i=1;i<=m;i++)
{
int fx=find(e[i].x);
int fy=find(e[i].y);
if(fx!=fy &&!vis[e[i].y])
{
vis[e[i].y]=1;
fa[fx]=fy;
ans+=e[i].z;
cnt++;
}
if(cnt==n-1)break;
}
if(cnt==n-1)cout<<ans<<endl;
else cout<<-1<<endl;
return 0;
}
B-变形课
相传搜索可以做 但是我是用最短路写的。输入一个字符串,首字母和尾字母建边,长度为1,跑一遍floyed,看一下B-M有没有路径就可
#include<bits/stdc++.h>
using namespace std;
const int inf=100;
int mp[40][40];
void intit()
{
for(int i=1;i<=26;i++)
{
for(int j=1;j<=26;j++)
{
mp[i][j]=inf;
if(i==j)mp[i][j]=0;
}
}
}
int main()
{
char s[1000];
intit();
while(scanf("%s",s)!=EOF)
{
if(s[0]=='0')
{
for(int k=1;k<=26;k++)
{
for(int i=1;i<=26;i++)
{
for(int j=1;j<=26;j++)mp[i][j]=min(mp[i][j],mp[i][k]+mp[k][j]);
}
}
if(mp[2]['m'-'a'+1]<inf)puts("Yes.");
else puts("No.");
intit();
}
else
{
int l=strlen(s);
mp[s[0]-'a'+1][s[l-1]-'a'+1]=1;
}
}
return 0;
}
C-Average Numbers
签到题 没什么好说的
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
int a[maxn];
int main()
{
int n;
cin>>n;
int tot=0;
for(int i=1;i<=n;i++)
{
cin>>a[i];
tot+=a[i];
}
vector<int> e;
for(int i=1;i<=n;i++)
{
if((tot-a[i])%(n-1)==0 && (tot-a[i])/(n-1)==a[i])e.push_back(i);
}
cout<<e.size()<<endl;
for(int i=0;i<e.size();i++)cout<<e[i]<<" ";
return 0;
}
D-Exposition
题目大意:求区间最大值和最小值小于等于K的最长子区间 输出区间起点和终点,如果有多个区间,全部输出
线段树存区间最大和最小值,然后以每一个点为起点,最后一点为终点,二分区间
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
const int inf=0x3f3f3f3f;
struct node
{
int l,r,mi,ma;
}tree[maxn<<2];
int ansx[maxn],ansy[maxn];
void build(int root,int l,int r)
{
tree[root].l=l;
tree[root].r=r;
if(l==r)
{
scanf("%d",&tree[root].mi);
tree[root].ma=tree[root].mi;
return;
}
int mid=(l+r)>>1;
build(root<<1,l,mid);
build(root<<1|1,mid+1,r);
tree[root].ma=max(tree[root<<1].ma,tree[root<<1|1].ma);
tree[root].mi=min(tree[root<<1].mi,tree[root<<1|1].mi);
}
int mi,ma;
void que(int root,int l,int r)
{
if(l<=tree[root].l && tree[root].r<=r)
{
mi=min(mi,tree[root].mi);
ma=max(ma,tree[root].ma);
return;
}
int mid=(tree[root].l+tree[root].r)>>1;
if(r<=mid)que(root<<1,l,r);
else if(l>mid)que(root<<1|1,l,r);
else que(root<<1,l,mid),que(root<<1|1,mid+1,r);
}
int main()
{
int n,k,len=0,cnt=0,a1=1;
scanf("%d%d",&n,&k);
build(1,1,n);
for(int i=1;i<=n;i++)
{
int l=i,r=n;
len=0;
while(l<=r)
{
int mid=(l+r)>>1;
mi=inf;
ma=-inf;
que(1,i,mid);
if(ma-mi >k)r=mid-1;
else
{
l=mid+1;
len=max(len,mid);
}
}
mi=inf;
ma=-inf;
que(1,i,len);
if(ma-mi<=k)
{
if(len-i+1 > a1)
{
a1=len-i+1;
cnt=1;
ansx[cnt]=i;
ansy[cnt]=len;
}
else if(len-i+1 == a1)
{
cnt++;
ansx[cnt]=i;
ansy[cnt]=len;
}
}
}
printf("%d %d\n",a1,cnt);
for(int i=1;i<=cnt;i++)printf("%d %d\n",ansx[i],ansy[i]);
return 0;
}
E-String Problem
最短路,每两个字母的转化看成一条有向边。先跑一边floyed求出每个字母转化成每个字母的最小费用,然后再开始遍历,如果有不能转化的字母输出-1
#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;
int mp[30][30];
int main()
{
string s1,s2,s;
cin>>s1>>s2;
s="";
int n,w;
char a,b;
cin>>n;
for(int i=1;i<=26;i++)
{
for(int j=1;j<=26;j++)
{
mp[i][j]=inf;
if(i==j)mp[i][j]=0;
}
}
for(int i=1;i<=n;i++)
{
cin>>a>>b>>w;
mp[a-'a'+1][b-'a'+1]=min(mp[a-'a'+1][b-'a'+1],w);
}
if(s1.size()!=s2.size())
{
cout<<-1<<endl;
return 0;
}
for(int k=1;k<=26;k++)
{
for(int i=1;i<=26;i++)
{
for(int j=1;j<=26;j++)
{
mp[i][j]=min(mp[i][j],mp[i][k]+mp[k][j]);
}
}
}
int ans=0;
for(int i=0;i<s1.size();i++)
{
if(s1[i]!=s2[i])
{
int tmp=1;
for(int j=1;j<=26;j++)
{
if((mp[s1[i]-'a'+1][j]+mp[s2[i]-'a'+1][j])<(mp[s1[i]-'a'+1][tmp]+mp[s2[i]-'a'+1][tmp]))tmp=j;
}
ans+=mp[s1[i]-'a'+1][tmp]+mp[s2[i]-'a'+1][tmp];
s+=('a'+tmp-1);
}
else
{
s+=s1[i];
}
if(ans>inf)break;
}
if(ans>inf)
{
cout<<-1<<endl;
return 0;
}
cout<<ans<<endl<<s<<endl;
return 0;
}
F-Knights
打表,每个点跟每个圆的位置关系,如果两点同在圆外或同在圆内,则不需要跨过栅栏,否则需要跨过。
#include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
const int maxn=1e3+10;
ll x1[maxn],x2[maxn],y11[maxn],y2[maxn],r[maxn];
bool vis[maxn][maxn];
int main()
{
ll n,m,k;
int x,y;
scanf("%lld%lld%lld",&n,&m,&k);
for(int i=1;i<=n;i++)scanf("%lld%lld",&x1[i],&y11[i]);
for(int i=1;i<=m;i++)scanf("%lld%lld%lld",&r[i],&x2[i],&y2[i]);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
ll d=(x1[i]-x2[j])*(x1[i]-x2[j])+(y11[i]-y2[j])*(y11[i]-y2[j]);
if(d<r[j]*r[j])vis[i][j]=1;
}
}
while(k--)
{
scanf("%d%d",&x,&y);
int ans=0;
for(int i=1;i<=m;i++)
{
if(vis[x][i]!=vis[y][i])ans++;
}
printf("%d\n",ans);
}
return 0;
}
G-Fire Again
BFS 队列最后一个就是答案,多点起火就在初始队列时,多放几个点进去就行了
文件输入加
freopen(“input.txt”,“r”,stdin);
freopen(“output.txt”,“w”,stdout);
就行
#include<bits/stdc++.h>
using namespace std;
const int maxn=2010;
struct node
{
int x,y;
};
bool vis[maxn][maxn];
int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};
int main()
{
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
int n,k,x,y,m;
scanf("%d%d%d",&n,&m,&k);
node p,ans,pp;
queue<node> q;
while(k--)
{
scanf("%d%d",&x,&y);
p.x=x;
p.y=y;
q.push(p);
vis[x][y]=1;
}
while(!q.empty())
{
ans=p=q.front();
q.pop();
for(int i=0;i<4;i++)
{
pp.x=p.x+dx[i];
pp.y=p.y+dy[i];
if(pp.x>0 && pp.x<=n && pp.y>0 && pp.y<=m && !vis[pp.x][pp.y])
{
vis[pp.x][pp.y]=1;
q.push(pp);
}
}
}
cout<<ans.x<<" "<<ans.y<<endl;
return 0;
}
H-Mysterious Present
最长上升子序列变形
先把能装卡片的信封都跳出来,再排个序,后面按最长上升子序列求一下就行了。
#include<bits/stdc++.h>
using namespace std;
const int maxn=5e3+11;
struct node
{
int x,y,id;
bool operator <(const node &k)const{
return x>k.x;
}
}e[maxn];
int dp[maxn][3];
int main()
{
int x,h,w,y,z,cnt=0,n;
scanf("%d%d%d",&n,&h,&w);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&y,&z);
if(y>h && z>w) //把能装卡片的信封挑出来
{
e[++cnt].x=y;
e[cnt].y=z;
e[cnt].id=i;
}
}
if(cnt==0)puts("0");
else
{
dp[1][0]=1;
sort(e+1,e+cnt+1);
for(int i=2;i<=cnt;i++)
{
for(int j=1;j<i;j++)
{
if(e[i].x<e[j].x && e[i].y<e[j].y && dp[i][0]<dp[j][0])
{
dp[i][1]=j;
dp[i][0]=dp[j][0];
}
}
dp[i][0]++;
}
int ans=1;
for(int i=1;i<=cnt;i++)
{
if(dp[i][0]>dp[ans][0])ans=i;
}
printf("%d\n",dp[ans][0]);
queue<int> s;
s.push(e[ans].id);
while(dp[ans][1])
{
ans=dp[ans][1];
s.push(e[ans].id);
}
while(!s.empty())
{
cout<<s.front()<<" ";
s.pop();
}
}
return 0;
}
I-Looking for Order
一个人在起点,有许多物品散落在各个地方,现在给出人起点的坐标和物品的坐标,然后给出一个要求,每次最多只能拿两个物品拿完物品必须回到原点装到包里面,求最短路程的方案,答案和路径都要输出。
这题如果是超级暴力即状态和两个点都全部枚举会超时,稍微优化下,因为先取哪个都是一样的,因为都 要回到原点,这样相当于每次是从原地出发找出然后回去,所以首先枚举状态,然后枚举第一个点,这时候先转移这个点的状态,然后再枚举第二个点,当枚举的第二个点都转移完状态后,就跳出第一个点的循环。
#include<bits/stdc++.h>
using namespace std;
const int maxn=(1<<24)+10;
int dp[maxn],pre[maxn],ans[100],x[30],y[30],mp[30][30];
int main()
{
int x0,y0,n,cnt=0;
cin>>x0>>y0>>n;
for(int i=0;i<n;i++)
{
cin>>x[i]>>y[i];
}
x[n]=x0,y[n]=y0;
for(int i=0;i<=n;i++)
{
for(int j=0;j<=n;j++)
{
mp[i][j]=(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]);
}
}
memset(dp,-1,sizeof(dp));
dp[0]=0;
for(int i=0;i< 1<<n ;i++)
{
if(dp[i]!=-1)
{
for(int j=0;j<n;j++)
{
if(!(i&(1<<j)))
{
int tmp=dp[i]+2*mp[n][j];
int t=(i|(1<<j));
if(dp[t]==-1 || dp[t]>tmp)
{
dp[t]=tmp;
pre[t]=i;
}
for(int k=0;k<n;k++)
{
if(!(t&(1<<k)))
{
int t1=t|(1<<k);
tmp=dp[i]+mp[n][j]+mp[j][k]+mp[k][n];
if(dp[t1]==-1 || dp[t1]>tmp)
{
dp[t1]=tmp;
pre[t1]=i;
}
}
}
break;
}
}
}
}
x0=(1<<n)-1;
cout<<dp[x0]<<endl;
int pr,tmp,a,b;
while(x0)
{
pr=pre[x0];
tmp=pr^x0;
a=b=0;
for(int i=0;i<n;i++)
{
if(tmp&(1<<i))
{
b=a;
a=i+1;
}
}
ans[++cnt]=0;
ans[++cnt]=a;
if(b)
{
ans[++cnt]=b;
}
x0=pr;
}
ans[++cnt]=0;
for(int i=1;i<=cnt;i++)cout<<ans[i]<<" ";
return 0;
}