陕西师范大学第九届ACM程序设计竞赛水题

Powered by:AB_IN 局外人

忘了有比赛了,赛后就先补三个题吧。

B ganmao病毒被消灭

有三个做法,第一反应还是 b f s bfs bfs,需要都跑一遍,所以就不用 v i s vis vis标记了。

  • b f s bfs bfs
    d i s [ i ] [ j ] dis[i][j] dis[i][j]标记 ( i , j ) (i,j) (i,j)走的最小值。如果有新的最小值出现,才让它入队列,并且更新 d i s [ i ] [ j ] dis[i][j] dis[i][j]
#include 
using namespace std;
int x2[4]={0,0,-1,1};
int y2[4]={1,-1,0,0};
const int inf=0x3f3f3f3f;
struct sa
{
    int x;
    int y;
    int step;
};
queue<sa>q;
int a[1010][1010],dis[1010][1010];
int n;
int bfs(int x,int y)
{
    int ans=inf;
    q.push({x,y,a[x][y]});
    while(!q.empty()){
        sa tmp;
        tmp=q.front();
        q.pop();
        int x3=tmp.x;
        int y3=tmp.y;
        if(x3==n&&y3==n)
            ans=min(ans,tmp.step);
        for(int i=0;i<4;i++){
            int x4=x3+x2[i];
            int y4=y3+y2[i];
            if(x4>=1 && x4<=n && y4>=1 && y4<=n && a[x4][y4]!=0)
            {
                int d=tmp.step+a[x4][y4];
                if(d<dis[x4][y4]){
                    dis[x4][y4]=d;
                    q.push({x4,y4,d});
                }
            }
        }
    }
    return ans;
}
int ans=inf;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            scanf("%d",&a[i][j]);
            dis[i][j]=inf;
        }
    }
    if(!a[1][1]||!a[n][n])
        printf("0");
    else{
        ans=bfs(1,1);
        if(ans==inf){
            printf("0");
        }
        else{
            printf("%d\n",ans);
        }
    }
    return 0;
}

d f s dfs dfs

#include 
using namespace std;
int x2[4]={0,0,-1,1};
int y2[4]={1,-1,0,0};
const int inf=0x3f3f3f;
int a[1010][1010],vis[1010][1010];
int n,ans=inf;
void dfs(int x,int y,int dis){
    if(x==n&&y==n){
        ans=min(ans,dis);
        return;
    }
    if(dis>=ans)return;
    for(int i=0;i<4;i++){
        int x3=x+x2[i];
        int y3=y+y2[i];
        if(x3<1||x3>n||y3<1||y3>n||a[x3][y3]==0||vis[x3][y3])
            continue;
        vis[x3][y3]=1;
        dfs(x3,y3,dis+a[x3][y3]);
        vis[x3][y3]=0;
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            scanf("%d",&a[i][j]);
        }
    }
    if(!a[1][1]||!a[n][n])
        printf("0");
    else{
        dfs(1, 1, a[1][1]);
        if(ans==inf) printf("0");
        else printf("%d",ans);
    }
    return 0;
}

D i j k s t r a Dijkstra Dijkstra

  • 这种矩阵的搜索也可以用最短路解决。
    先放张图
    陕西师范大学第九届ACM程序设计竞赛水题_第1张图片
    平时 D i j k s t r a Dijkstra Dijkstra都是解决下面这种问题,但其实可以转化成上面这种点的权值的问题。
    平时都是 d i s [ x ] = 0 dis[x]=0 dis[x]=0,对应起点处为 0 0 0,而此时这个题起点处有别的值,那么一开始赋上就行了。
  • 然后就是点的问题。二维转一维。 ( i , j ) − > ( i ∗ n + j ) (i,j)->(i*n+j) (i,j)>(in+j)
    即可。
    显而易见, 起 点 ( 1 , 1 ) − > ( n + 1 ) 终 点 ( n , n ) − > ( n ∗ n + n ) 起点(1,1)->(n+1) \\ 终点(n,n)->(n*n+n) (1,1)>(n+1)(n,n)>(nn+n)
#include
using namespace std;

namespace IO{
    char ibuf[1<<21],*ip=ibuf,*ip_=ibuf;
    char obuf[1<<21],*op=obuf,*op_=obuf+(1<<21);
    inline char gc(){
        if(ip!=ip_)return *ip++;
        ip=ibuf;ip_=ip+fread(ibuf,1,1<<21,stdin);
        return ip==ip_?EOF:*ip++;
    }
    inline void pc(char c){
        if(op==op_)fwrite(obuf,1,1<<21,stdout),op=obuf;
        *op++=c;
    }
    inline int read(){
        register int x=0,ch=gc(),w=1;
        for(;ch<'0'||ch>'9';ch=gc())if(ch=='-')w=-1;
        for(;ch>='0'&&ch<='9';ch=gc())x=x*10+ch-48;
        return w*x;
    }
    template<class I>
    inline void write(I x){
        if(x<0)pc('-'),x=-x;
        if(x>9)write(x/10);pc(x%10+'0');
    }
    class flusher_{
    public:
        ~flusher_(){if(op!=obuf)fwrite(obuf,1,op-obuf,stdout);}
    }IO_flusher;
}
using namespace IO;


const int maxn=1e6+10;
const int inf=0x3f3f3f3f;
struct sa{
    int dis;
    int pos;
};
bool operator <(const sa &a,const sa &b) { return a.dis>b.dis; }

priority_queue<sa>q;


struct Edge
{
    int to, w, next;
}edge[maxn];
int head[maxn];
int cnt;
void add_edge(int u, int v, int w)
{
    edge[cnt].to = v;
    edge[cnt].w = w;
    edge[cnt].next = head[u];
    head[u] = cnt++;
}


int dis[maxn];
bool vis[maxn];
int n,m,s,u,v,w;

void dijkstra(int s,int v){
    memset(vis,0,sizeof(vis));
    memset(dis,0x3f,sizeof(dis));
    dis[s]=v;
    q.push( (sa) {0,s});
	while(!q.empty())
	{
		sa ns=q.top();
		q.pop();
		int x=ns.pos;
		if(vis[x]) continue;
		vis[x]=1;
		for(int i=head[x]; i!=-1 ; i=edge[i].next)
		{
			if(dis[edge[i].to]>dis[x]+edge[i].w)
			{
				dis[edge[i].to]=dis[x]+edge[i].w;
				q.push( (sa) {dis[edge[i].to],edge[i].to});
			}
		}
	}
}
int a[110][110];
int x2[4]={0,0,-1,1};
int y2[4]={1,-1,0,0};
int main()
{
    n=read();
    memset(head,-1,sizeof(head));
	for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            a[i][j]=read();
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++){
            if(a[i][j]){
                for(int h=0;h<4;h++){
                    int x3=i+x2[h];
                    int y3=j+y2[h];
                    if(x3>=1 && x3<=n && y3>=1 && y3<=n && a[x3][y3]){
                        add_edge(i*n+j, x3*n+y3, a[x3][y3]);
                    }
                }
            }
        }
    dijkstra(n+1,a[1][1]);
    if(dis[n*n+n]==inf) printf("0");
    else printf("%d",dis[n*n+n]);
	return 0;
}

D younik要排号

签到。

l=set()
cnt=0
for _ in range(int(input())):
    s=input()
    if s not in l:
        cnt+=1
        l.add(s)
    if s=="younik":
        print(cnt)
        break  

F 新冠病毒要回家

次短路!!! 顾名思义就是第二短的路
边可以重复走!

  • 1 1 1为起点跑最短路,以 n n n为起点跑最短路。
  • 枚举每一条边,次短路只可能是两种情况:
    • 最短路上的最短的一条边跑两次
    • 除了最短路外的其他能到 n n n的次短路。
#include
using namespace std;

namespace IO{
    char ibuf[1<<21],*ip=ibuf,*ip_=ibuf;
    char obuf[1<<21],*op=obuf,*op_=obuf+(1<<21);
    inline char gc(){
        if(ip!=ip_)return *ip++;
        ip=ibuf;ip_=ip+fread(ibuf,1,1<<21,stdin);
        return ip==ip_?EOF:*ip++;
    }
    inline void pc(char c){
        if(op==op_)fwrite(obuf,1,1<<21,stdout),op=obuf;
        *op++=c;
    }
    inline int read(){
        register int x=0,ch=gc(),w=1;
        for(;ch<'0'||ch>'9';ch=gc())if(ch=='-')w=-1;
        for(;ch>='0'&&ch<='9';ch=gc())x=x*10+ch-48;
        return w*x;
    }
    template<class I>
    inline void write(I x){
        if(x<0)pc('-'),x=-x;
        if(x>9)write(x/10);pc(x%10+'0');
    }
    class flusher_{
    public:
        ~flusher_(){if(op!=obuf)fwrite(obuf,1,op-obuf,stdout);}
    }IO_flusher;
}
using namespace IO;


const int maxn=1e6+10;
const int inf=0x3f3f3f3f;
struct sa{
    int dis;
    int pos;
};
bool operator <(const sa &a,const sa &b) { return a.dis>b.dis; }

priority_queue<sa>q;


struct Edge
{
    int u, v, w, next;
}edge[maxn<<2];
int head[maxn];
int cnt;
void add_edge(int u, int v, int w)
{
    edge[cnt].u = u;
    edge[cnt].v = v;
    edge[cnt].w = w;
    edge[cnt].next = head[u];
    head[u] = cnt++;
}


int dis1[maxn],dis2[maxn];
bool vis[maxn];
int n,m,s,u,v,w;

void dij(int s,int v,int d[]){
    memset(vis,0,sizeof(vis));
    for (int i=0;i<=n;i++) d[i] = inf;
    d[s]=v;
    q.push( (sa) {0,s});
	while(!q.empty())
	{
		sa ns=q.top();
		q.pop();
		int x=ns.pos;
		if(vis[x]) continue;
		vis[x]=1;
		for(int i=head[x]; i!=-1 ; i=edge[i].next)
		{
			int to=edge[i].v;
			int dd=d[x]+edge[i].w;
			if(d[to]>dd)
			{
				d[to]=dd;
				q.push( (sa) {d[to],to});
			}
		}
	}
}

int main()
{
    memset(head,-1,sizeof(head));
	cin>>n>>m;
	for(int i=1; i<=m; i++){
        cin>>u>>v>>w;
        u++;v++;//题目是0到n-1,++之后就成了1到n了。
		add_edge(u,v,w);
        add_edge(v,u,w);
        //不用(v+n,u+n,w+n),因为cnt一直在++,统计每个边,顺下去即可。
	}
	dij(1,0,dis1);//从1开始跑最短路
	dij(n,0,dis2);//从n开始跑最短路
    int ans1=dis1[n],ans2=inf,min_d=inf;
	for(int i=1;i<=cnt;i++){
        u=edge[i].u; v=edge[i].v; w=edge[i].w;
        if((dis1[u]+w) +dis2[v]> ans1) 
        //dis1[u]意思就是从1->u的最短路
        //dis2[v]意思就是从n->v的最短路
        //中间就还差了一个u->v的w
        //如果大于最短路,那么就先取最小值
            ans2= min(ans2,(dis1[u]+w) +dis2[v]);
        if((dis1[u]+w) +dis2[v]== ans1) 
        //如果这个就是最短路,那么遍历它的每一条边,求出所有边的最小值
            min_d=min(min_d,w);
    }
    cout<<min(ans2,ans1+min_d*2);
	return 0;
}

完结。

你可能感兴趣的:(ACM,dijkstra,dfs)