Codeforces #303 Div 2 简要题解

比赛总结

第一次AK掉div 2,非常开心,罚时也还好。
比赛中的提交记录

在正式和非正式选手中排名179名(共6932人)

在正式选手里排名27名(共2826人)

A. Toy Cars

题目链接

http://codeforces.com/contest/545/problem/A

题目大意

给出每个车 i 和其他车 j 的碰撞情况( i 撞毁、 j 撞毁、 ij 均撞毁、 ij 均未撞毁),问有多少个车在和其他任意车碰撞后,都不会撞毁

思路

水题,就不多说了

代码

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <vector>

#define MAXN 110

using namespace std;

int mat[MAXN][MAXN],n;
int sol[MAXN],top=0;

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            scanf("%d",&mat[i][j]);
    for(int i=1;i<=n;i++)
    {
        bool flag=true;
        for(int j=1;j<=n;j++)
            if(mat[i][j]==1||mat[i][j]==3)
        {
            flag=false;
            break;
        }
        if(flag) sol[++top]=i;
    }
    printf("%d\n",top);
    if(!top) return 0;
    for(int i=1;i<=top;i++)
        printf("%d ",sol[i]);
    printf("\n");
    return 0;
}

B. Equidistant String

题目链接

http://codeforces.com/contest/545/problem/B

题目大意

定义两个串 S T 的差别度 diff(S,T) 为, S T SiTi i 的个数
要你构造一个串 A ,使得 diff(A,S)=diff(A,T)

思路

水题,首先看 S T 有多少位不相同,若不相同的位的个数 tot 为奇数则显然无解,否则构造一个串 A ,其中若 Si=Ti ,则 Ai=Si ,前 tot2 SiTi 的位置填 Si ,后 tot2 SiTi 的位置填 Ti

代码

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <vector>

#define MAXN 110000

using namespace std;

char A[MAXN],B[MAXN];
int n;

int main()
{
    scanf("%s",A+1);
    scanf("%s",B+1);
    n=strlen(A+1);
    int tot=0;
    for(int i=1;i<=n;i++)
        if(A[i]!=B[i])
            tot++;
    if(tot&1)
    {
        printf("impossible\n");
        return 0;
    }
    tot/=2;
    for(int i=1,t=1;i<=n;i++)
    {
        if(A[i]==B[i]) printf("%c",A[i]);
        else if(t<=tot) {printf("%c",A[i]); t++;}
        else printf("%c",B[i]);
    }
    printf("\n");
    return 0;
}

C. Woodcutters

题目链接

http://codeforces.com/contest/545/problem/C

题目大意

一共有 n 个树,你要从第一个树一直砍到最后一个树。对于每个高度为 hi 、树根在 xi 的树,你可以选择拔除它,留下区间为 [xi,xi] 的坑,或者让它往左边倒,留下区间为 [xihi,xi] 的坑,或者让它往右边倒,留下区间为 [xi,xi+hi] 的坑,但是砍树的前提是,留下的新坑不能和以前任何一个老坑相交,问最多能让多少个树倒下而不是拔除

思路

i 个树能否倒下,取决于第 i1 个树的状态,因此我们可以做 O(n) 的DP。用 f[i][0] 表示第 i 个树往左倒,最多能倒下的树的个数,用 f[i][1] 表示第 i 个树拔除掉,最多能倒下的树的个数,用 f[i][2] 表示第 i 个树往右倒,最多能倒下的树的个数

然后剩余要做的事就是各种特判了。

代码

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <vector>

#define MAXN 110000

using namespace std;

typedef long long int LL;

int n;
LL h[MAXN],x[MAXN];
int f[MAXN][3];

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%I64d%I64d",&x[i],&h[i]);
    f[1][0]=f[1][1]=f[1][2]=1;
    for(int i=2;i<=n;i++)
    {
        //往左倒
        if(x[i]-h[i]>x[i-1]+h[i-1]) f[i][0]=max(f[i][0],f[i-1][2]+1);
        if(x[i]-h[i]>x[i-1])
        {
            f[i][0]=max(f[i][0],f[i-1][1]);
            f[i][0]=max(f[i][0],f[i-1][0]+1);
        }
        //拔出
        if(x[i]>x[i-1]) f[i][1]=max(f[i][1],f[i-1][1]);
        if(x[i]>x[i-1]+h[i-1]) f[i][1]=max(f[i][1],f[i-1][2]+1);
        if(x[i]>x[i-1]) f[i][1]=max(f[i][1],f[i-1][0]+1);
        //往右倒
        if(x[i]>x[i-1]+h[i-1]) f[i][2]=max(f[i][2],f[i-1][2]+1);
        if(x[i]>x[i-1])
        {
            f[i][2]=max(f[i][2],f[i-1][1]);
            f[i][2]=max(f[i][2],f[i-1][0]+1);
        }
    }
    printf("%d\n",max(max(f[n][0],f[n][1]),f[n][2]));
    return 0;
}

D. Queue

题目链接

http://codeforces.com/contest/545/problem/D

题目大意

给你 n 个人,每个人要去银行窗口花 ti 的时间办事,每个人的等待时间是其在队列里前面的人的 ti 之和。一个人如果其 ti 大于等于其等待时间的话,他就高兴,否则不高兴。问排列整个队列后,最多能让多少个人高兴。

思路

非常水的DP,显然要让 ti 尽量小的人尽量放在队列的前面。因此我们可以首先对 ti 进行排序,然后维护当前的等待时间( ti 前缀和),依次将 ti 从小到大放入队列里,并更新高兴的人的个数。但是需要注意,如果当前的 ti 比当前的等待时间小的话,那么现在无论如何都无法满足这个人的需求了,就需要把这个人放到队列的最末端,这样的话,就不会耽误他后面的人的等待时间了,还可以多满足一些后面的人。刚开始我就因为这个问题wa了一发。

代码

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <vector>
#include <queue>

#define MAXN 110000

using namespace std;

typedef long long int LL;

//priority_queue<LL>heap;
int n;
LL t[MAXN];

int main()
{
    int ans=0;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%I64d",&t[i]);
    sort(t+1,t+n+1);
    //reverse(t+1,t+n+1);
    LL nowt=0;
    for(int i=1;i<=n;i++)
    {
        if(t[i]<nowt) continue;
        ans++;
        nowt+=t[i];
    }
    printf("%d\n",ans);
    return 0;
}

E. Paths and Trees

题目链接

http://codeforces.com/contest/545/problem/E

题目大意

给你一个无向图,要你找出一个生成树,使得生成树里 S 到其他任何点的最短路和原图里 S 到其他任何点的最短路长度一样。找出满足这样的条件的最小的生成树。

思路

首先,kruscal的做法显然是错误的,具体可以看第二个样例。

我们不妨用单源最短路算法先求出一个从 S 出发的最短路树,显然这样的树是满足要求的。但是还不能满足这个生成树最小的要求。

不妨改进堆优化Dijsktra。堆中不仅记录点和点到S的距离,还要加上一个标记:这个点是从哪条边走过来的。这样的话,假如在做dijsktra时,我们同时看到有两个点到 S 的最短路相同,我们就可以优先把前驱边权较小的那个点加入最短路树。

代码

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <vector>
#include <queue>

#define MAXN 610000

using namespace std;

typedef long long int LL;

struct edge
{
    int u,v,id,next;
    LL w;
}edges[MAXN];

int n,m;

int head[MAXN],nCount=1;

void AddEdge(int U,int V,LL W,int ID)
{
    edges[++nCount].u=U;
    edges[nCount].v=V;
    edges[nCount].w=W;
    edges[nCount].id=ID;
    edges[nCount].next=head[U];
    head[U]=nCount;
}

LL dist[MAXN];
bool vis[MAXN];
bool used[MAXN];
int pre[MAXN];

struct Info
{
    int u;
    LL w;
    LL dis;
    Info(int _u,LL _d,LL _w):u(_u),dis(_d),w(_w){}
};

bool operator<(Info a,Info b)
{
    if(a.dis==b.dis) return a.w<b.w;
    return a.dis<b.dis;
}

bool operator>(Info a,Info b)
{
    if(a.dis==b.dis) return a.w>b.w;
    return a.dis>b.dis;
}

priority_queue<Info,vector<Info>,greater<Info> >heap;

void heapdij(int S)
{
    memset(dist,0x3f,sizeof(dist));
    dist[S]=0;
    heap.push(Info(S,0,0));
    while(!heap.empty())
    {
        Info now=heap.top();
        heap.pop();
        int u=now.u;
        if(vis[u]) continue;
        vis[u]=true;
        for(int p=head[u];p!=-1;p=edges[p].next)
        {
            int v=edges[p].v;
            if(dist[u]+edges[p].w<=dist[v])
            {
                if(pre[v]!=-1) used[pre[v]]=false;
                pre[v]=edges[p].id;
                used[edges[p].id]=true;
                dist[v]=dist[u]+edges[p].w;
                heap.push(Info(v,dist[v],edges[p].w));
            }
        }
    }
}

int main()
{
    memset(head,-1,sizeof(head));
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int u,v;
        LL w;
        scanf("%d%d%I64d",&u,&v,&w);
        AddEdge(u,v,w,i);
        AddEdge(v,u,w,i);
    }
    int S;
    scanf("%d",&S);
    heapdij(S);
    LL tot=0;
    for(int i=1;i<=m;i++)
        if(used[i]) tot+=(LL)edges[i*2].w;
    printf("%I64d\n",tot);
    for(int i=1;i<=m;i++)
        if(used[i])
            printf("%d ",i);
    printf("\n");
    return 0;
}

你可能感兴趣的:(Codeforces #303 Div 2 简要题解)