F.One-Way-Roads
题意是这样
有一个连通双向边的图,要求给每个边定一个方向,问如何可以使最大入度的那个点入度最小。
这样与混合欧拉回路有异曲同工之妙。
我的想法
要把每个点的入度限制,就相当与建立网络流1-n个点,每个点连向汇点,容量为d。
那么就相当于限制了最大入度为d。
然后对于每一条边建立一个新的结点,由源点指向其,然后其指向该边的两点,
意义为这个边只能连接一个点,且容量为1,然后二分d跑最大流判断最小d可以使得容量=边数,也就是每个边都确定了入度的方向即可。
这题补了一整天,与ar进行对拍,发现自己菜到怀疑板子的错误。(这里了解到了一个更快的网络流isap板子
源码:
#include<cstdio>
#include<cmath>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>
#include<set>
#include<queue>
#include<iostream>
using namespace std;
const int maxn=55;
const int Ni = 1550;
const int MAX = 1<<26;
struct Edge{
int u,v,c;
int next;
}edge[30*Ni];
int n,m;
int edn;//边数
int p[Ni];//父亲
int d[Ni];
int sp,tp;//原点,汇点
void addedge(int u,int v,int c)
{
edge[edn].u=u; edge[edn].v=v; edge[edn].c=c;
edge[edn].next=p[u]; p[u]=edn++;
edge[edn].u=v; edge[edn].v=u; edge[edn].c=0;
edge[edn].next=p[v]; p[v]=edn++;
}
int bfs()
{
queue <int> q;
memset(d,-1,sizeof(d));
d[sp]=0;
q.push(sp);
while(!q.empty())
{
int cur=q.front();
q.pop();
for(int i=p[cur];i!=-1;i=edge[i].next)
{
int u=edge[i].v;
if(d[u]==-1 && edge[i].c>0)
{
d[u]=d[cur]+1;
q.push(u);
}
}
}
return d[tp] != -1;
}
int dfs(int a,int b)
{
int r=0;
if(a==tp)return b;
for(int i=p[a];i!=-1 && r<b;i=edge[i].next)
{
int u=edge[i].v;
if(edge[i].c>0 && d[u]==d[a]+1)
{
int x=min(edge[i].c,b-r);
x=dfs(u,x);
r+=x;
edge[i].c-=x;
edge[i^1].c+=x;
}
}
if(!r)d[a]=-2;
return r;
}
int dinic(int sp,int tp)
{
int total=0,t;
while(bfs())
{
while(t=dfs(sp,MAX))
total+=t;
}
return total;
}
struct ttt{
int u,v;
};
ttt qq[100500];
int res(int x){
edn=0;
int i;
memset(p,-1,sizeof(p));
int t1=n;
for(i=1;i<=m;i++){
t1++;
addedge(0,t1,1);
addedge(t1,qq[i].u,1);
addedge(t1,qq[i].v,1);
}
tp=++t1;
for(i=1;i<=n;i++){
addedge(i,tp,x);
}
int res=dinic(sp,tp);
if(res==m)
return 1;
else
return 0;
}
int main(){
freopen("in.txt","r",stdin);
freopen("out2.txt","w",stdout);
int i,j,k,f1,f2,f3,f4,t1,t2,t3,t4;
int T;
int s,t;
scanf("%d",&T);
while(T--){
scanf("%d",&n);
scanf("%d",&m);
sp=0;
memset(p,-1,sizeof(p));
for(i=1;i<=m;i++){
scanf("%d %d",&qq[i].u,&qq[i].v);
}
int right1,left1,mid1;
left1=0;right1=25050;
t2=1e9;
while(right1>=left1){
mid1=(left1+right1)/2;
t1=res(mid1);
if(t1==1){
t2=min(t2,mid1);
right1=mid1-1;
}else{
left1=mid1+1;
}
}
cout << t2 << endl;
}
return 0;
}
之前错误的想法(错误说明建图失败!!!
刚开始是想着建立拆点成2列建立,然后右边那一列连向汇点,且容量为d。
然后对于每一条边新建立一个结点,这个结点连向左边那列边的两头,意义为每个边只能进入到一个点,然后连向右边意义为入度,建两列的意义是因为有可能从A->B,也有可能B->A。
这样的话会出现如下错误
只看下面那部分 相当4-5与3-4这2条边 下面那个2含义为对于3-4这条边 3连向了4 上面那个1含义就成了4-5这条边 的入度连向了3~ 不符合要的那个想法 所以有错
然后下面是ar的源码,再次orz ar老师,日常吊打
#include <bits/stdc++.h>
using namespace std;
const int N = 6005;
const int inf = 0x3f3f3f3f;
struct edge{
int from,to,cap,flow;
edge(){}
edge(int u,int v,int w){
from=u,to=v,cap=w,flow=0;
}
};
vector<int>G[N];
vector<edge>es;
int S,T,n,m;
int d[N],cur[N];
bool vis[N];
int add(int u,int v,int w){
es.push_back(edge(u,v,w));
es.push_back(edge(v,u,0));
int ss=es.size();
G[u].push_back(ss-2);
G[v].push_back(ss-1);
return ss-2;
}
bool bfs(){
memset(vis,0,sizeof(vis));
d[S]=0;vis[S]=1;
queue<int>q;
q.push(S);
while(!q.empty()){
int x=q.front();q.pop();
for(int i=0;i<G[x].size();++i){
edge& e=es[G[x][i]];
if(!vis[e.to]&&e.cap>e.flow){
vis[e.to]=1;
d[e.to]=d[x]+1;
q.push(e.to);
}
}
}
return vis[T];
}
int dfs(int x,int a){
if(x==T||a==0)return a;
int flow=0,f;
for(int& i=cur[x];i<G[x].size();++i){
edge& e=es[G[x][i]];
if(d[x]+1==d[e.to]&&( f=dfs(e.to,min(a,e.cap-e.flow)))>0){!
flow+=f;
e.flow+=f;
es[G[x][i]^1].flow-=f;
a-=f;
if(a==0)break;
}
}
return flow;
}
int mf(){
int flow=0;
while(bfs()){
memset(cur,0,sizeof(cur));
flow+=dfs(S,inf);
}
return flow;
}
void clear(){
for(int i=0;i<N;i++) G[i].clear();
es.clear();
}
int idn[505][2];
int idm[2505][2];
int dd[505];
int clk;
vector<int> ans;
vector<pair<int,int> >Ms;
int TT;
void rebuild(int Md){
ans.clear();
clear();
for(int i=1;i<=n;i++)
add(S, idn[i][0], inf);
for(int i=1;i<=n;i++)
add(idn[i][1], T, min(Md,dd[i]));
for(size_t i=0;i<Ms.size();i++){
int xx = Ms[i].first;
int yy = Ms[i].second;
add(idn[xx][0],idm[i][0],1);
add(idn[yy][0],idm[i][0],1);
add(idm[i][1],idn[xx][1],1);
add(idm[i][1],idn[yy][1],1);
ans.push_back(add(idm[i][0], idm[i][1],1));
}
}
int main(){
freopen("in.txt","r",stdin);
freopen("out1.txt","w",stdout);
scanf("%d", &TT);
S=0;T=1;
int res=0;
while(TT--){
// cin >>res;
scanf("%d%d",&n,&m);
// cout << n <<" " << m << endl;
clk=2;
clear();
ans.clear();
Ms.clear();
memset(idn,0,sizeof idn);
memset(idm, 0,sizeof idm);
memset(d,0, sizeof d);
memset(dd,0,sizeof dd);
memset(cur,0,sizeof cur);
for(int i=1;i<=n;i++) idn[i][0]=++clk, idn[i][1]=++clk;
for(int i=0;i<m;i++){
int xx,yy;
scanf("%d%d", &xx,&yy);
dd[xx]++;
dd[yy]++;
Ms.push_back(make_pair(xx, yy));
idm[i][0]=++clk;
idm[i][1]=++clk;
}
int maxd = *max_element(dd+1, dd+1+n);
for(int ansd=0;ansd<=maxd;ansd++){
rebuild(ansd);
mf();
assert(ans.size()==m);
bool ok = 1;
for(size_t i=0;i<ans.size();i++){
ok &= es[ans[i]].flow;
}
if(ok){
//cout <<res <<"is"<< t2 << endl;
cout << ansd << endl;
//printf("%dis%d\n",res,ansd);
break;
}
}
}
return 0;
}
然后这里是对拍造数据的源码(渣渣苟活都活不下去了
#include <iostream>
#include <stdio.h>
#include <vector>
#include <string.h>
#include <algorithm>
#include <math.h>
#include <map>
#include <set>
#include <queue>
#include <stdlib.h>
#include <time.h>
using namespace std;
typedef long long ll;
const ll Mod=1e9+7;
const ll maxn=1e5+7;
//int qq[maxn];
int pre[maxn];
void init(int n){
for(int i=0;i<=n;i++)
pre[i]=i;
}
int find1(int x){
int r=x;
while(r!=pre[r]){
r=pre[r];
}
return r;
}
int find2(int x,int y){
int x1=find1(x);
int y1=find1(y);
if(pre[x1]!=pre[y1]){
pre[y1]=x1;
return 0;
}
return 1;
}
int map1[505][505];
//vectot<int>q1;
int main(){
freopen("in.txt","w",stdout);
int i,j,k,f1,f2,f3,f4,t1,t2,t3,t4,t5,n,m;
srand((unsigned int)(time(NULL)));
t1=1;
t2=2;
int T;
T=10000;
cout << T<< endl;
while(T--){
int max1=50;
int max2=2500;
//cout << T<< endl;
//while(T--){
n=((rand()%max1)*5)%max1+1;
if(n<10)n+=10;
init(n);
m=((rand()%max1)*5)%max1+1;
t3=1;
cout << n<< " " << m+n-1 << endl;
for(i=2;i<=n;i++){
t1=rand()%(i-1)+1;
cout<< t1 <<" " <<i << endl;
}
while(m--){
t1=((rand()%max1)*5)%n+1;
t2=((rand()%max1)*5)%n+1;
if(t1==t2){
m++;continue;
}
cout <<t1 <<" " << t2<<endl;
}
}
//cout << t1+t2 << " " << t3 << endl;
return 0;
}