BestCoder Round #1 解题报告

来源:互联网 发布:sabayon linux 中文 编辑:程序博客网 时间:2024/06/06 14:22

最近听到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

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

原创粉丝点击