Crab Graphs
题目大意:
定义螃蟹图是这样的无向图:有n+1个点,其中一个点(head)连接其他的n个点(foot),另外n个点与head点相连。
给你一个无向图和整数k,问其中至多有几个**不超过**k个feet的不相交的螃蟹图?
题目分析:
之前在hihocoder里面见过一个拆点构建网络流的题,这里又见到一道。不过还是没太挖掘出这类题目的共性。
算法如下:
1. 把原图中每个点i拆成2i和2i+1两个点
2. 如果原图中存在边 <i,j> ,则在新图里连上 <2i,2j+1>,<2j,2i+1> ,流量为1
3. 取源点为1,连接1和所有2i的点,流量为k
4. 取汇点为2n+2,连接2i+1和汇点,流量为1
5. 最大流即为所求。
其实知道了这个模型,好像也确实是这么回事,不过想到这个建模应该是本题的难点,剩下的都是套模板,没啥好说的。这方面的题还是做得太少,而且网络流在笔试面试中只见过一道,一般出现在ACM中~
#include <bits/stdc++.h>
using namespace std;
#define RE(x) freopen(x,"r",stdin)
#define WR(x) freopen(x,"w",stdout)
#define INF 0x3f3f3f3f
typedef long long ll;
int c,n,t,m;
struct Edge {
int next,to,flow;
}edge[40005];
int head[1230],p,level[1230];
int deg[123];
void addedge(int u,int v,int w) {
edge[p].to=v;
edge[p].flow=w;
edge[p].next=head[u];
head[u]=p;p++;
edge[p].to=u;
edge[p].flow=0;
edge[p].next=head[v];
head[v]=p;p++;
}
void init() {
memset(head,-1,sizeof(head));
memset(deg,0,sizeof(deg));
memset(edge,0,sizeof(edge));
p=0;
}
bool bfs(int s,int t) {
memset(level,0,sizeof(level));
queue<int> q;
q.push(s);
level[s]=1;
while(!q.empty()) {
int u=q.front();
q.pop();
if(t==u)
return true;
for(int i=head[u];i!=-1;i=edge[i].next) {
int v=edge[i].to,f=edge[i].flow;
if(level[v] == 0 && f!=0) {
q.push(v);
level[v]=level[u]+1;
}
}
}
return false;
}
int dfs(int u,int maxf,int t) {
if(u==t)
return maxf;
int temp=0;
for(int i=head[u];i!=-1&&temp<maxf;i=edge[i].next) {
int v=edge[i].to,f=edge[i].flow;
if(level[v] == level[u]+1 && f!=0) {
f=dfs(v,min(maxf-temp,f),t);
edge[i].flow-=f;edge[i^1].flow+=f;temp+=f;
}
}
if(!temp)
level[u]=INF;
return temp;
}
int dinic(int s,int t) { //源点s,汇点t,求最大流
ll ans=0;
while(bfs(s,t))
ans+=dfs(s,INF,t);
return ans;
}
int main() {
cin>>c;
while(c--) {
init();
cin>>n>>t>>m;
int u,v;
for(int i=0;i<m;i++) {
cin>>u>>v;
deg[u]++;deg[v]++;
addedge(2*u,2*v+1,1);
addedge(2*v,2*u+1,1);
}
for(int i=1;i<=n;i++) {
addedge(1,2*i,t);
addedge(2*i+1,2*n+2,1);
}
cout<<dinic(1,2*n+2)<<endl;
}
}
problem-solving
题目大意:
有n道题,第i道题的难度为i,评分为r[i].
每一天,你可以选择其中的若干道题来做,有两个要求:
* 难度必须是递增的;
* 相邻两道题的评分差距不能小于K
问至少几天完成全部的题?
题目分析:
把这n道题看做n个点,对 i<j,|r[i]−r[j]|≥k的点对加有向边,然后得到一张dag图,(不要问我为什么没环,自己想去!!)答案就是这个图的最小路径覆盖。
拆点构建二分图,跑匈牙利算法或者网络流,都行。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define RE(x) freopen(x,"r",stdin)
#define WR(x) freopen(x,"w",stdout)
#define INF 0x3f3f3f3f
const int M = 1e9+7;
const double PI = acos(-1.0);
int t,n,k;
int v[1005];
int f[2005];
bool vis[2005];
vector<int> g[2010];
void addedge(int i,int j) {
g[2*i-1].push_back(2*j);
g[2*j].push_back(2*i-1);
}
bool dfs(int i) {
vis[i]=1;
for(int j=0;j<g[i].size();j++) {
int u=g[i][j];
if(f[u]==-1 || (!vis[f[u]] && dfs(f[u]))) {
f[u]=i;
f[i]=u;
return true;
}
}
return false;
}
int main() {
//RE("in.txt");WR("out.txt");
cin>>t;
while (t--) {
cin>>n>>k;
memset(g,0,sizeof(g));
for(int i=1;i<=n;i++) {
cin>>v[i];
}
for(int i=1;i<=n;i++) {
for(int j=i+1;j<=n;j++) {
if(abs(v[i]-v[j])>=k)
addedge(i,j);
}
}
memset(f,-1,sizeof(f));
int cnt=0;
for(int i=1;i<=n*2;i++) {
if(f[i]==-1) {
memset(vis,0,sizeof(vis));
cnt+=dfs(i);
}
}
cout<<n-cnt<<endl;
}
}
总结一下,这俩题在算法实现上都非常简单,难点在于如何用图建模,感觉自己脑子已经生锈了。。。。。。
本文介绍了两道算法题目,一是寻找螃蟹图的最大数量,二是计算完成所有任务所需的最少天数。通过图论与网络流算法解决,涉及拆点构建网络流及匈牙利算法。

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



