这半年就像什么都没有学到一样,周赛只能做做水题,感觉跟每次周赛爆零没什么区别。
跟其他人的差距太大了,还是自己平时不努力的缘故,以后还是要加油啊。
希望以后周赛能通过自己的思考真正做出题目,而不只是做做签到题。
C - Borg Maze
题目大意:就是求最小生成树,套模板就可以,然后建图需要用到bfs。
但是因为之前学的都不认真,学过的算法也只是知道一点点而已,连bfs也不会写,唉,感觉真的tcl,努力啊。
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<cmath>
#include<queue>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
int a[505][505],d[505],n,m,cnt;
bool v[505];
int math[505][505],dis[505][505];
bool vis[505][505];
struct node
{
int x,y,step;
node(int xx,int yy,int Step):x(xx),y(yy),step(Step){}
};
void bfs(int x,int y)
{
int sx[]={1,-1,0,0},sy[]={0,0,1,-1};
memset(vis,0,sizeof(vis));
memset(dis,0,sizeof(dis));
queue<node>q;
q.push(node(x,y,0));
vis[x][y]=1;
while(!q.empty()){
node p=q.front();q.pop();
if(math[p.x][p.y]){
a[math[x][y]][math[p.x][p.y]]=dis[p.x][p.y];
}
for(int i=0;i<4;i++){
int x1=p.x+sx[i],y1=p.y+sy[i];
if(vis[x1][y1]||math[x1][y1]==-1)continue;
if(x1<0||y1<0||x1>=m||y1>=n)continue;
vis[x1][y1]=1;
dis[x1][y1]=dis[p.x][p.y]+1;
q.push(node(x1,y1,dis[x1][y1]));
}
}
}
void prim(){
memset(v,0,sizeof(v));
memset(d,0x3f,sizeof(d));
d[1]=0;
for(int i=1;i<cnt;i++){
int x=0;
for(int j=1;j<=cnt;j++)
if(!v[j]&&(x==0||d[j]<d[x]))
x=j;
v[x]=1;
for(int y=1;y<=cnt;y++)
if(!v[y])
d[y]=min(d[y],a[x][y]);
}
}
int main() {
int t;cin>>t;
while(t--){
cin>>n>>m;
for(int i=1;i<=n;i++)a[i][i]=0;
memset(math,0,sizeof(math));
for(int i=0;i<55;i++)
for(int j=0;j<55;j++)
math[i][j]=-1;
char s[100];cnt=0;
gets(s);
for(int i=0;i<m;i++){
gets(s);
for(int j=0;j<n;j++){
if(s[j]==' ')math[i][j]=0;
else if(s[j]=='S'||s[j]=='A'){
math[i][j]=++cnt;
}
else math[i][j]=-1;
}
}
for(int i=0;i<m;i++)
for(int j=0;j<n;j++)
if(math[i][j]>0)
bfs(i,j);
prim();
int ans=0;
for(int i=1;i<=cnt;i++)ans+=d[i];
cout<<ans<<endl;
}
}
D - 0 or 1
这道题读了好久也没看懂题意,只是隐隐感觉可能跟最短路有关。
然后看了其他人的题解才明白,其实如果一开始的时候仔细看一下样例给的矩阵就会发现这就是在给邻接矩阵。
1.X 12+X 13+...X 1n=1 第1个点的出度为1
2.X 1n+X 2n+...X n-1n=1 第n个点的入度为1
3.for each i (1<i<n), satisfies ∑X ki (1<=k<=n)=∑X ij (1<=j<=n). 第 i个点的入度和出度相等
最后还有一种情况要考虑,比如这样:
1 1 1 0 0 0
1 1 1 0 0 0
1 1 1 0 0 0
0 0 0 1 1 1
0 0 0 1 1 1
0 0 0 1 1 1
这种情况下就不是求最小生成树了,以第1个点为起点的最小环+以第n个点为起点的最小环;
判断一下这两种情况哪个更小就可以了。
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
int a[310][310],d[310],n;
bool v[310];
void spfa(int m)
{
queue<int>q;
for(int i=1;i<=n;i++){
if(i==m){
d[i]=inf;
v[i]=0;
}
else{
d[i]=a[m][i];
v[i]=1;
q.push(i);
}
}
while(!q.empty()){
int x=q.front();
q.pop();
v[x]=0;
for(int y=1;y<=n;y++){
if(d[y]>d[x]+a[x][y]){
d[y]=d[x]+a[x][y];
if(!v[y]){
q.push(y);v[y]=1;
}
}
}
}
}
int main()
{
while(scanf("%d",&n)!=EOF){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
scanf("%d",&a[i][j]);
}
}
spfa(1);
int d1=d[1],d2=d[n];
spfa(n);
int d3=d[n];
printf("%d\n",min(d2,d1+d3));
}
return 0;
}
E - Extended Traffic
这道题就是求最短路,如果结果小于 3 则输出 ?。因为有负权值,所以用 spfa ,然后遇到负环,就将该环上所有的点标记一下,结果都为?,一开始没有标记就一直超时。
找到进入队列n次以上的点,用dfs搜索以该点为起点的边所指向的终点,将其全部标记。
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<cmath>
#include<queue>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
struct edge{
ll v,w,next;
}edge[50005];
ll head[30000],d[505],in[505],c[505];
bool vis[505];
ll n,m,cnt;
void add(ll u,ll v,ll w)
{
edge[++cnt].v=v;
edge[cnt].w=w;
edge[cnt].next=head[u];
head[u]=cnt;
}
void dfs(ll v)
{
c[v]=1;
for(int i=head[v]; i!=-1; i=edge[i].next)
if(!c[edge[i].v])
dfs(edge[i].v);
}
void spfa()
{
memset(vis,0,sizeof(vis));
memset(in,0,sizeof(in));
memset(d,inf,sizeof(d));
memset(c,0,sizeof(c));
d[1]=0;vis[1]=1;in[1]++;
queue<int>q;
q.push(1);
while(!q.empty()){
int x=q.front();q.pop();
vis[x]=0;
for(int i=head[x];i!=-1;i=edge[i].next){
int y=edge[i].v;
if(c[y])continue;
if(d[y]>d[x]+edge[i].w){
d[y]=d[x]+edge[i].w;
if(!vis[y]){
q.push(y);vis[y]=1;in[y]++;
}
if(in[y]>n)dfs(y);
}
}
}
}
int main()
{
ll t;scanf("%lld",&t);
ll math[205];
for(int i=1;i<=t;i++){
for(ll i=1;i<=1000;i++)head[i]=-1;
scanf("%lld",&n);
for(ll i=1;i<=n;i++){
scanf("%lld",&math[i]);
}
scanf("%lld",&m);
ll x,y,z;
cnt=0;
for(ll i=1;i<=m;i++){
scanf("%lld%lld",&x,&y);
z=(math[y]-math[x])*(math[y]-math[x])*(math[y]-math[x]);
add(x,y,z);
}
spfa();
ll q,k;scanf("%lld",&q);
printf("Case %d:\n",i);
for(ll i=1;i<=q;i++){
scanf("%lld",&k);
if(c[k]||d[k]<3||d[k]>=inf)printf("?\n");
else printf("%lld\n",d[k]);
}
}
}
F - The Unique MST
这道题就是求最小生成树是否唯一,一开始想到要删边,但是wa了。
然后看了题解,发现思路正确,但是还有很多细节要改进。
a的代码是参考了别人的,但自己也算是完全理解了。
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<cmath>
#include<algorithm>
#include<queue>
#include<istream>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
int fa[110],n,m;
bool v[110];
struct rec
{ //same标记排序后是否与后面的一条边权值相同
int x,y,z; //del标记当前暂时删除的一条边
bool used,del,same; //used标记第一次求出最小生成树的边
bool operator<(const rec &b)const//运算符重定义
{
return z<b.z;
}
}edge[10010];
bool first; //first标记是否是未删边之前的操作,求最小生成树时需要
int get(int x)
{
if(x==fa[x])return x;
return fa[x]=get(fa[x]);
}
int Kruskal()
{
int ans=0;
for(int i=1;i<=n;i++)fa[i]=i;
for(int i=1;i<=m;i++){
if(edge[i].del==true)continue;//如果当前操作删除i边,则遇到该边跳过
int x=get(edge[i].x);
int y=get(edge[i].y);
if(x==y)continue;
fa[x]=y;
ans+=edge[i].z;
if(first) //只有第一次调用函数需要标记i边为最小生成树的边
edge[i].used=true;
}
return ans;
}
void mark_same_edge() //标记是否有权值相同的边
{
for(int i=1;i<=m;i++){
if(edge[i].z==edge[i-1].z){
edge[i-1].same=true;
}
}
}
int main() {
int t;cin>>t;
bool tag;
while(t--){
cin>>n>>m;
tag=false;
memset(edge,0,sizeof(edge));//初始化边
for(int i=1;i<=m;i++){
scanf("%d%d%d",&edge[i].x,&edge[i].y,&edge[i].z);
edge[i].del=false,edge[i].same=false;
}
sort(edge+1,edge+m+1);
first=true;
int ans=Kruskal(),ans0;
first=false;
mark_same_edge();
for(int i=1;i<=m;i++){
if(edge[i].used==true&&edge[i].same==true){
//该边必须满足存在于最小生成树中且图中存在与它相同权值的边
edge[i].del=true;
ans0=Kruskal();
if(ans==ans0){
cout<<"Not Unique!"<<endl;
tag=true;
break;
}
}
}
if(!tag)cout<<ans<<endl;
}
}