最近听到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
题目分析:计算几何不可做……

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

被折叠的 条评论
为什么被折叠?



