您的位置:首页 > 其它

01分数规划三类型总结

2017-01-16 08:33 113 查看
一、生成树型

给定一个带权无向图,每条边有两个权值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
>=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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: