BestCoder Round #1 解题报告

本文解析了BestCoder平台上的四道竞赛题目,包括“逃生”、“项目管理”、“海岸线”和“新海岛计划”。通过图论、网络流等算法解决实际问题,提供了详细的解题思路及代码实现。

最近听到SPLAY学长说有一个网站叫BestCoder,题目质量挺不错,而且难度在NOIP提高组水平,比较适合我刷(重点是不像CF那样要翻墙,深夜打比赛,而且它还支持在HDU上提交)。于是我有空跑过去看了看,见到只有6页比赛嘛,于是决定从头开始做……但愿暑假能刷完吧。


T1:逃生

题目传送门:http://bestcoder.hdu.edu.cn/contests/contest_showproblem.php?cid=525&pid=1001

题目分析:刚打开这题就见到了陈老师……一开始我觉得让编号小的尽量靠前=字典序最小,然后觉得很水嘛,直到后来我举出了反例。然后我开始脑洞大开YY,但想出来的方法基本都是时间复杂度玄学。我感觉从前往后放有困难,于是试图从后面开始做。如果a要在b之前,就b向a连一条边,然后每一次选编号最大的入度为0的点,并删去它的出边,就可以保证编号小的尽可能靠前。我感觉这个方法跟我做过的一道POJ上的递归打印欧拉路径的题有点像,直觉告诉我这样做和原问题是等价的,但我不会证明。于是我上网找了一下,发现好像这就是正解,然而也没有严格的证明……总之用优先队列找编号最大,时间O(nlog(n))(像我这种从来不用STL库的人当然只能手写heap辣)

CODE:

#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;

const int maxn=30010;
const int maxm=100100;

struct edge
{
    int obj;
    edge *Next;
} e[maxm];
edge *head[maxn];
int cur;

int pin[maxn];
int ans[maxn];

int Heap[maxn];
int tail;

int t,n,m;

void Add(int x,int y)
{
    cur++;
    e[cur].obj=y;
    e[cur].Next=head[x];
    head[x]=e+cur;
}

void Insert(int x)
{
    Heap[++tail]=x;
    int i=tail;
    while (i>1)
    {
        int j=i>>1;
        if (Heap[i]>Heap[j]) swap(Heap[i],Heap[j]);
        else break;
        i=j;
    }
}

int Get()
{
    int x=Heap[1];
    Heap[1]=Heap[tail--];
    int i=1;
    while (1)
    {
        int son=i;
        int temp=i<<1;
        if ( temp<=tail && Heap[temp]>Heap[son] ) son=temp;
        temp++;
        if ( temp<=tail && Heap[temp]>Heap[son] ) son=temp;
        if (son==i) break;
        swap(Heap[i],Heap[son]);
        i=son;
    }
    return x;
}

int main()
{
    freopen("c.in","r",stdin);
    freopen("c.out","w",stdout);

    scanf("%d",&t);
    while (t--)
    {
        scanf("%d%d",&n,&m);
        cur=-1;
        for (int i=1; i<=n; i++) head[i]=NULL;
        for (int i=1; i<=m; i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            Add(b,a);
            pin[a]++;
        }

        tail=0;
        for (int i=1; i<=n; i++)
            if (!pin[i]) Insert(i);
        for (int i=n; i>=1; i--)
        {
            int x=Get();
            for (edge *p=head[x]; p; p=p->Next)
            {
                int y=p->obj;
                pin[y]--;
                if (!pin[y]) Insert(y);
            }
            ans[i]=x;
        }

        for (int i=1; i<n; i++) printf("%d ",ans[i]);
        printf("%d\n",ans[n]);
    }

    return 0;
}

T2:项目管理

题目传送门:http://bestcoder.hdu.edu.cn/contests/contest_showproblem.php?cid=525&pid=1002

题目分析:这题比第一题要简单一些吧。由于图是联通的,而且边数只比点数多10,那么我们做一遍DFS,非树边的个数顶多11条。DFS的时候我们记valson[node]表示node的儿子的权值和。修改node的权值的时候改它本身的val值,和它父亲的valson值即可。查询答案的时候将它father的val加上它的valson,再暴力查看非树边即可。

CODE:

#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;

const int maxn=100100;
const int maxm=13;

struct edge
{
    int obj;
    bool vis;
    edge *Next,*rev;
} e[maxn<<1];
edge *head[maxn];
int cur;

int cnt[maxn][maxm];
int val[maxn];

int fa[maxn];
int vson[maxn];

int t,n,m,q;

void Add(int x,int y)
{
    cur++;
    e[cur].obj=y;
    e[cur].vis=false;
    e[cur].rev=&e[cur+1];
    e[cur].Next=head[x];
    head[x]=e+cur;

    cur++;
    e[cur].obj=x;
    e[cur].vis=false;
    e[cur].rev=&e[cur-1];
    e[cur].Next=head[y];
    head[y]=e+cur;
}

void Dfs(int node)
{
    for (edge *p=head[node]; p; p=p->Next)
        if (!p->vis)
        {
            p->vis=p->rev->vis=true;
            int son=p->obj;
            if (!fa[son]) fa[son]=node,Dfs(son);
            else cnt[node][ ++cnt[node][0] ]=son,cnt[son][ ++cnt[son][0] ]=node;
        }
}

int main()
{
    freopen("c.in","r",stdin);
    freopen("c.out","w",stdout);

    scanf("%d",&t);
    while (t--)
    {
        scanf("%d%d",&n,&m);
        cur=-1;
        for (int i=1; i<=n; i++) head[i]=NULL,fa[i]=val[i]=vson[i]=cnt[i][0]=0;
        for (int i=1; i<=m; i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            Add(x,y);
        }

        fa[1]=1;
        Dfs(1);

        scanf("%d",&q);
        while (q--)
        {
            int cmd;
            scanf("%d",&cmd);
            if (!cmd)
            {
                int u,v;
                scanf("%d%d",&u,&v);
                val[u]+=v;
                if (u>1) vson[ fa[u] ]+=v;
            }
            else
            {
                int u;
                scanf("%d",&u);
                int ans=vson[u];
                if (u>1) ans+=val[ fa[u] ];
                for (int i=1; i<=cnt[u][0]; i++) ans+=val[ cnt[u][i] ];
                printf("%d\n",ans);
            }
        }
    }

    return 0;
}

T3:海岸线

题目传送门:http://bestcoder.hdu.edu.cn/contests/contest_showproblem.php?cid=525&pid=1003

题目分析:一道网络流好题啊。一开始我完全没想到是最小割,往贪心,DP方面想了好久。上网浏览了一下题解,知道了是最小割,再回去想,还是不知道怎么建图。后来仔细看了题解,上面说它跟《最小割模型在信息学竞赛中的应用》里面的一道例题 最优标号 Optimal Marks 很像,于是我跑过去把论文读了一遍,会做例题了,然而这题还是想不到怎么做……
我们先在网格外围一层”D”,然后这题的关键就在于最大化相邻格子中一个是陆地,一个是海洋的数量,即最小化相邻格子相同的数量。在论文中讨论了如何最小化相邻格子不同 ,但对于一个普通的图,要做到最大化不同格子,是无法建图跑网络流的。然而这里我们是网格图,我们可以对其进行黑白染色,将所有黑色的格子反转(即陆地->深海,深海->陆地,浅海->浅海)。这样就变成了论文中的情况。
这道题的难点在于往最小割方面去想,以及颜色反转。反转了之后就是一个很简单的模型,我们让每一个格子都为一个点,如果是深海,则S向其连无穷大的边,它向T连1;浅海则两个都为1;陆地则S连1,T连无穷大;相邻格子之间连一条容量为1的无向边。最后用格子数+相邻格子对数-最大流即可。

CODE:

#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;

const int maxn=52;
const int maxv=10010;
const int maxm=1000000;
const int oo=1000000000;

struct edge
{
    int obj,cap;
    edge *Next,*rev;
} e[maxm];
edge *head[maxv];
int cur;

edge *nhead[maxv];
int level[maxv];
int que[maxv];
int he,ta;
int T;

char s[maxn];
int map[maxn][maxn];
int t,n,m;

int Get(int x,int y)
{
    return x*(m+2)+y+1;
}

void Add(int x,int y,int xf,int yf)
{
    cur++;
    e[cur].obj=y;
    e[cur].cap=xf;
    e[cur].rev=&e[cur+1];
    e[cur].Next=head[x];
    head[x]=e+cur;

    cur++;
    e[cur].obj=x;
    e[cur].cap=yf;
    e[cur].rev=&e[cur-1];
    e[cur].Next=head[y];
    head[y]=e+cur;
}

bool Bfs()
{
    for (int i=0; i<=T; i++) nhead[i]=head[i],level[i]=0;
    he=0,ta=1,que[1]=0,level[0]=1;
    while (he<ta)
    {
        int node=que[++he];
        for (edge *p=head[node]; p; p=p->Next)
        {
            int son=p->obj;
            if ( p->cap && !level[son] ) level[son]=level[node]+1,que[++ta]=son;
        }
    }
    return level[T];
}

int Dfs(int node,int maxf)
{
    if ( node==T || !maxf ) return maxf;
    int nowf=0;
    for (edge *&p=nhead[node]; p; p=p->Next)
    {
        int son=p->obj;
        if ( p->cap && level[son]==level[node]+1 )
        {
            int d=Dfs(son, min(maxf,p->cap) );
            p->cap-=d;
            p->rev->cap+=d;
            maxf-=d;
            nowf+=d;
            if (!maxf) break;
        }
    }
    if (maxf) level[node]=0;
    return nowf;
}

int Dinic()
{
    int max_flow=0;
    while ( Bfs() ) max_flow+=Dfs(0,oo);
    return max_flow;
}

int main()
{
    freopen("c.in","r",stdin);
    freopen("c.out","w",stdout);

    scanf("%d",&t);
    for (int g=1; g<=t; g++)
    {
        scanf("%d%d",&n,&m);
        for (int i=1; i<=n; i++)
        {
            scanf("%s",&s);
            for (int j=0; j<m; j++)
            {
                if (s[j]=='.') map[i][j+1]=0;
                if (s[j]=='E') map[i][j+1]=1;
                if (s[j]=='D') map[i][j+1]=2;
            }
        }

        for (int i=0; i<=m+1; i++) map[0][i]=map[n+1][i]=2;
        for (int i=1; i<=n; i++) map[i][0]=map[i][m+1]=2;
        for (int i=0; i<=n+1; i++) for (int j=0; j<=m+1; j++)
            if ( (i+j)&1 ) map[i][j]=2-map[i][j];

        /*for (int i=0; i<=n+1; i++)
        {
            for (int j=0; j<=m+1; j++) printf("%d ",map[i][j]);
            printf("\n");
        }*/

        T=(n+2)*(m+2)+1;
        cur=-1;
        for (int i=0; i<=T; i++) head[i]=NULL;
        for (int i=0; i<=n+1; i++) for (int j=0; j<=m+1; j++)
        {
            int id=Get(i,j);
            if (map[i][j]==0) Add(0,id,oo,0),Add(id,T,1,0);
            if (map[i][j]==1) Add(0,id,1,0),Add(id,T,1,0);
            if (map[i][j]==2) Add(0,id,1,0),Add(id,T,oo,0);
        }

        for (int i=0; i<=n+1; i++)
            for (int j=0; j<=m+1; j++)
            {
                int x=Get(i,j);
                if (i<=n) Add(x, Get(i+1,j) ,1,1);
                if (j<=m) Add(x, Get(i,j+1) ,1,1);
            }

        int ans=Dinic();
        ans=(n+1)*(m+2)+(n+2)*(m+1)+(n+2)*(m+2)-ans;
        printf("Case %d: %d\n",g,ans);
    }

    return 0;
}

T4:新海岛计划

题目传送门:http://bestcoder.hdu.edu.cn/contests/contest_showproblem.php?cid=525&pid=1004

题目分析:计算几何不可做……

关于 KFCOI Round #1 的具体资料并未在提供的引用中提及。然而,可以根据 Codeforces 类型的比赛结构以及类似的竞赛经验来推测可能的内容。 ### 参与 KFCOI Round #1 的相关信息 通常情况下,在线编程比赛(如 KFCOI 或 Codeforces)会提供以下资源供参赛者回顾: #### 1. **题目集** 比赛结束后,官方一般会在其官方网站或相关平台上发布完整的题目集合。这些题目可能会附带样例输入输出以便于理解问题的要求[^1]。 #### 2. **题解与思路分析** 官方或者社区成员往往会撰写详细的题解文档,帮助未通过某些难题的选手学习新的算法技巧。例如,在 Codeforces 中,像 A 到 D 题这样的解析是非常常见的。 #### 3. **比赛数据统计** 统计信息包括但不限于每位选手提交次数、正确率等指标。这类数据分析有助于评估个人表现并与他人对比进步空间[^3]。 #### 4. **赛后讨论论坛** 很多平台都设有专门区域让参与者自由交流想法甚至分享错误经历以互相借鉴成长。比如提到过的多次尝试不同方法解决同一道简单题目的过程就是很好的例子。 ### 如何获取上述材料? - 如果您已经注册参加了该赛事,则可以直接登录账户查看存档页面; - 对于公开可用的部分链接地址可以通过搜索引擎查询关键词“KFCOI Round #1 review”找到相关内容; - 加入一些活跃的技术社群也可能获得更多一手资讯和支持。 以下是基于假设场景下的 Python 实现示例用于处理类似 CF 圆整数分解成最小数量正整数组合的问题: ```python def min_sum_representation(n): result = "" current_value = n for digit in range(9, 0, -1): while current_value >= digit: result += str(digit) current_value -= digit return &#39;&#39;.join(sorted(result)) print(min_sum_representation(7)) # 输出:"7" ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值