一、生成树型
给定一个带权无向图,每条边有两个权值a[i],b[i],求图的一个生成树,使得sigam(a[i])/sigam(b[i])最小.
解析:我们假设已经找到了一个解k,一个新的解比k更优
那么sigam(a[i])/sigam(b[i])<=k
即sigam(a[i])-k*sigam(b[i])<=0
sigam(a[i]-k*b[i])<=0
然后我们只需要求不等式左边的sigam(a[i]-b[i])的最小值ans,比较ans和0的关系就能判断当前解是否最优。
那么我们算法的流程就是这样了:
(1)二分一个k
(2)用新的边权a[i]-k*b[i]建图
(3)求最小生成树
(4)判断当前解是否最优
例:【jzyzoj1636】老司机飙车
现在有N个群需要张锳杰老司机的资源,为了方便表示,我们用1到N这N个数字来表示这N个群。张锳杰现在在群1,而由于网警的原因,在每个群之间传输资源都有特定的需要耗费的时间和所承担的危险,而且同一时间内资源只能在两个群之间传递。为了凸显程序员的逼格,我们将每个群在一个三维空间内以坐标的形式表示,定义两个群之间传输资源所需时间为这两个群在xOy平面内投影的直线距离,而危险则被定义为两个群第三维坐标之差。张锳杰想要单位时间内的平均危险最小,也就是传输总时间与承担的总危险的比值最低,请你帮老司机找出这个比值
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<iomanip>
using namespace std;
#define eps 0.0005
#define FILE "read"
#define MAXN 1010
#define up(i,j,n) for(int i=j;i<=n;i++)
#define dn(i,j,n) for(int i=j;i>=n;i--)
namespace INIT{
char buf[1<<15],*fs,*ft;
inline char getc(){return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;}
inline int read(){
int x=0,f=1; char ch=getc();
while(!isdigit(ch)) {if(ch=='-') f=-1; ch=getc();}
while(isdigit(ch)) {x=x*10+ch-'0'; ch=getc();}
return x*f;
}
}using namespace INIT;
struct node{
int x,y,h;double v;
bool operator<(const node pp) const {return v<pp.v;}
}e[MAXN*MAXN],E[MAXN*MAXN];
int n,len,Len,X[MAXN],Y[MAXN],V[MAXN],f[MAXN];
bool cmp(node a,node b) {return a.v<b.v;}
int find(int x) {return f[x]==x?x:f[x]=find(f[x]);}
void insert(int i,int j){
e[++len].x=i; e[len].y=j;
e[len].v=sqrt((double)((X[i]-X[j])*(X[i]-X[j])+(Y[i]-Y[j])*(Y[i]-Y[j])));
e[len].h=abs(V[i]-V[j]);
}
void Insert(int x,int y,double v){E[++Len].x=x; E[Len].y=y; E[Len].v=v;}
double check(double k){
Len=0;
up(i,1,len) Insert(e[i].x,e[i].y,e[i].h-k*e[i].v);
up(i,1,n) f[i]=i;
sort(E+1,E+Len+1);
double ans=0; int sum=0;
up(i,1,Len){
int p=find(E[i].x),q=find(E[i].y);
if(p!=q) f[p]=q,ans+=E[i].v,sum++;
if(sum==n-1) break;
}
return ans;
}
int main(){
n=read();
up(i,1,n){
X[i]=read(); Y[i]=read(); V[i]=read();
up(j,1,i-1) insert(i,j);
}
double left=0.0001,right=10;
while(left+0.0003<=right){
double mid=(left+right)/2.0;
if(check(mid)<=0) right=mid;
else left=mid;
}
double ans=check(left)?left:right;
printf("%.3lf\n",ans);
return 0;
}
二、最小割型
给定一个带权无向图,要使得从S到T没有路径相连,需要删除C条边,求这C条边的权值和sigam(vi)/C的最小值。
解析:与上题一样,我们还是假设已经找到了一个解k,那么一个更优解需要满足的条件是sigam(vi)/C<=k
即sigam(vi)-C*k<=0
sigam(vi-k)<=0
那么我们只需要求出不等式左边的最小值,判断它与0的关系就行了。
那么算法的流程如下:
(1)二分一个k值
(2)用新的边权vi-k建图
(3)跑最小割
(4)判断当前解是否最优
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<iomanip>
using namespace std;
#define FILe "read"
#define INF 1000000000
#define eps 0.00001
#define MAXM 4050
#define MAXN 1050
#define up(i,j,n) for(int i=j;i<=n;i++)
#define dn(i,j,n) for(int i=j;i>=n;i--)
const double oo=9999999999999ll;
namespace INIT{
char buf[1<<15],*fs,*ft;
inline char getc(){return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;}
inline int read(){
int x=0,f=1; char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
}using namespace INIT;
struct node{int x,y,next,rel;double v;}e[MAXM*4];
int n,m,len,top,Link[MAXN],q[MAXN],level[MAXN],X[MAXN],Y[MAXN],V[MAXN],stack[MAXN],col[MAXN],vis[MAXN];\
bool comp(double a,double b){return abs(a-b)<eps;}
void insert(int x,int y,double v){
e[++len].next=Link[x];Link[x]=len;e[len].y=y;e[len].v=v;e[len].rel=len+1;
e[++len].next=Link[y];Link[y]=len;e[len].y=x;e[len].v=0;e[len].rel=len-1;
}
bool bfs(){
memset(level,-1,sizeof(level));
int head=0,tail=1; level[1]=0; q[1]=1;
while(++head<=tail){
for(int i=Link[q[head]];i;i=e[i].next){
if(!comp(e[i].v,0)&&level[e[i].y]<0){
q[++tail]=e[i].y;
level[q[tail]]=level[q[head]]+1;
}
}
}
return level[n]>=0;
}
double MAXFLOW(int x,double flow){
if(x==n) return flow;
double maxflow=0,d=0;
for(int i=Link[x];i&&maxflow<flow;i=e[i].next){
if(level[x]+1==level[e[i].y]&&!comp(e[i].v,0)){
if(d=MAXFLOW(e[i].y,min(e[i].v,flow-maxflow))){
e[i].v-=d; e[e[i].rel].v+=d; maxflow+=d;
}
}
}
if(!maxflow) level[x]=-1;
return maxflow;
}
double solve(){
double d,sum=0;
while(bfs()) while(d=MAXFLOW(1,oo*1.0)) sum+=d;
return sum;
}
bool check(double k){
double sum=0;len=0;top=0;
memset(Link,0,sizeof(Link));
memset(vis,0,sizeof(vis));
up(i,1,m) {
double temp=V[i]-k;
if(temp<=0) sum+=temp,stack[++top]=i,vis[i]=1;
else insert(X[i],Y[i],temp),insert(Y[i],X[i],temp);
}
sum+=solve();
return sum<=0;
}
void dfs(int x){
if(col[x]) return;
col[x]=1;
for(int i=Link[x];i;i=e[i].next)if(!comp(e[i].v,0)) dfs(e[i].y);
}
int main(){
n=read(); m=read();
up(i,1,m) X[i]=read(),Y[i]=read(),V[i]=read();
double left=1,right=1000000000;
while(left+eps<right){
double mid=(left+right)/2.0;
if(check(mid)) right=mid;
else left=mid;
}
if(!check(left)) check(right);
dfs(1);
up(i,1,m) if(col[X[i]]!=col[Y[i]]&&!vis[i]) stack[++top]=i;
printf("%d\n",top);
sort(stack+1,stack+top+1);
up(i,1,top) printf("%d ",stack[i]);
//printf("\n%d\n",clock());
return 0;
}