01分数规划
bzoj1486 最小圈
題目大意:求一個圖內的某個環,使得sigma ai[i]/k(環上點數)最小。
思路:二分答案,如果sigma ai[i]-k*mid>0說明mid可以更大,每次判斷的時候給所有邊-mid,就成了判斷負環的問題。這里用spfa普通的判法會tle,所以要用深搜版的spfa來判斷。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxm 3005 #define maxe 20005 #define eps 1e-10 using namespace std; int n,point[maxm]={0},next[maxe]={0},en[maxe]={0},tot=0; double va[maxe],vv[maxe]={0},dis[maxm]; bool visit[maxm],flag; int cmp(double x,double y){if (x-y>eps) return 1;if (y-x>eps) return -1;return 0; } void add(int u,int v,double w){next[++tot]=point[u];point[u]=tot;en[tot]=v;vv[tot]=w; } void spfa(int u){int i,j,v;visit[u]=true;for (i=point[u];i;i=next[i]){if (dis[v=en[i]]>dis[u]+va[i]){if (visit[v]){flag=true;break;}else{dis[v]=dis[u]+va[i];spfa(v);}}}visit[u]=false; } bool judge(double x){int i,j;flag=false;memset(dis,0,sizeof(dis));memset(visit,false,sizeof(visit));for (i=1;i<=tot;++i) va[i]=vv[i]-x;for (i=1;i<=n;++i){spfa(i);if (flag) return true;}return false; } int main(){int i,j,m,u,v;double w,l=0.0,r=0.0,mid;scanf("%d%d",&n,&m);for (i=1;i<=m;++i){scanf("%d%d%lf",&u,&v,&w);add(u,v,w*1.0);r=max(r,w*1.0);l=min(l,w*1.0);}while(cmp(l,r)<0){mid=(l+r)/2;if (judge(mid))r=mid;else l=mid;}printf("%.8f\n",l); }View Code
?
bzoj3232 圈地游戲
題目大意:給定一個網格,格內和邊上都有權值,求一個圈,使得格內的權值和v/邊上的權值和c最大。
思路:二分答案k,判斷能否存在v-ck>0,就是求所有格內權值-不選的格的權值-邊界上選的格在邊上的權值-選的兩個格間的邊權,然后就是總的格內的價值-最小割,S表示選,T表示不選,那么從S向邊界的點連邊界的邊權,相鄰兩點連中間邊權,每個點向T連格內邊權。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxm 5000 #define maxe 100000 #define maxx 55 #define inf 2100000000.0 #define eps 1e-9 using namespace std; struct use{int st,en;double va; }edge[maxe]; double ai[maxx][maxx],he[maxx][maxx],li[maxx][maxx],sum=0; int point[maxm],next[maxe],dis[maxm],gap[maxm],pre[maxm],bi[maxx][maxx],tot,n,m,st,en,dx[4]={0,1,0,-1},dy[4]={1,0,-1,0},cur[maxm]; void add(int u,int v,double vv){next[++tot]=point[u];point[u]=tot;edge[tot]=(use){u,v,vv};next[++tot]=point[v];point[v]=tot;edge[tot]=(use){v,u,0.0}; } double isap(){int i,j,u,v;bool f;double ans=0,minn;memset(dis,0,sizeof(dis));memset(gap,0,sizeof(gap));for (i=st;i<=en;++i) cur[i]=point[i];gap[0]=en-st+1;u=st;while(dis[st]<en-st+1){f=false;for (i=cur[u];i;i=next[i])if (edge[i].va-0>eps&&dis[edge[i].en]+1==dis[u]){cur[u]=i;f=true;break;}if (f){pre[u=edge[i].en]=i;if (u==en){minn=inf;for (i=en;i!=st;i=edge[pre[i]].st)minn=min(minn,edge[pre[i]].va);ans+=minn;for (i=en;i!=st;i=edge[pre[i]].st){edge[pre[i]].va-=minn;edge[pre[i]^1].va+=minn;}u=st;}}else{--gap[dis[u]];if (!gap[dis[u]]) return ans;j=en-st+1;for (i=point[u];i;i=next[i])if (edge[i].va-0>eps)j=min(j,dis[edge[i].en]);++gap[dis[u]=j+1];cur[u]=point[u];if (u!=st) u=edge[pre[u]].st;}}return ans; } bool judge(double x){int i,j,k,xi,yi;tot=1;memset(point,0,sizeof(point));memset(next,0,sizeof(next));for (i=1;i<=m;++i){add(st,bi[1][i],x*he[1][i]);add(st,bi[n][i],x*he[n+1][i]);}for (i=1;i<=n;++i){add(st,bi[i][1],x*li[i][1]);add(st,bi[i][m],x*li[i][m+1]);}for (i=1;i<=n;++i)for (j=1;j<=m;++j) add(bi[i][j],en,ai[i][j]);for (i=1;i<=n;++i)for (j=1;j<=m;++j)for (k=0;k<4;++k){xi=i+dx[k];yi=j+dy[k];if (xi<1||xi>n||yi<1||yi>m) continue;if (k==0) add(bi[i][j],bi[xi][yi],x*li[i][j+1]);if (k==1) add(bi[i][j],bi[xi][yi],x*he[i+1][j]);if (k==2) add(bi[i][j],bi[xi][yi],x*li[i][j]);if (k==3) add(bi[i][j],bi[xi][yi],x*he[i][j]);}return (sum-isap()>eps); } int main(){int i,j;double l,r,mid;scanf("%d%d",&n,&m);st=tot=1;en=n*m+2;for (i=1;i<=n;++i)for (j=1;j<=m;++j){scanf("%lf",&ai[i][j]);bi[i][j]=++tot;sum+=ai[i][j];}for (i=1;i<=n+1;++i)for (j=1;j<=m;++j) scanf("%lf",&he[i][j]);for (i=1;i<=n;++i)for (j=1;j<=m+1;++j) scanf("%lf",&li[i][j]);l=0.0;r=250000.0;while(r-l>eps){mid=(l+r)/2.0;if (judge(mid)) l=mid;else r=mid;}printf("%.3f\n",l); }View Code
?
bzoj2285 保密
題目大意:給定n個點和m條有向邊(有時間ti和安全系數si),點是兩排點,前n1個點間有一些連接兩排點的空腔,一條路徑的安全度是sigma(ti)/sigma(si),如果一只軍隊能從n到一個點,就能訪問過所有這個點連出去的空腔,求訪問過所有空腔的最小的安全度和。
思路:分數規劃+網絡流。對于每個有空腔連接的點,分數規劃求出從n到每一個點的最小安全度(二分答案mid,最短路,sigma(ti)-midsigma(si)<0,可以用map存一下每一種二分的mid,算過就不用算了,這樣可以節省計算量)。然后就是從兩排點中選出一些點覆蓋所有的空腔且權值和最小,這是最小點權覆蓋的模型,從s向第一排點連邊權值,從第二排向終點連邊權值,將空腔所連接的兩個點連邊inf,求出最小割,如果大于inf就是無解,否則就輸出。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<map> #define maxm 705 #define maxe 1000005 #define eps 1e-5 #define LD double #define inf 2100000000. #define len 1000000 using namespace std; struct use{int st,en;double va,si;}edge[maxe]; int point[maxm]={0},next[maxe],tot,gap[maxm]={0},dis[maxm]={0},pre[maxm]={0},n,que[len+1],st,en,cur[maxm],tt=0; LD dq[maxm],di[10005][maxm]; bool visit[maxm]={false},vi[maxm]={false}; map<double,int>cnt; int cmp(LD x,LD y){if (x-y>eps) return 1;if (y-x>eps) return -1;return 0; } void add(int u,int v,LD vv,LD va){next[++tot]=point[u];point[u]=tot;edge[tot]=(use){u,v,vv,va}; } void add2(int u,int v,LD vv){next[++tot]=point[u];point[u]=tot;edge[tot]=(use){u,v,vv,0.};next[++tot]=point[v];point[v]=tot;edge[tot]=(use){v,u,0.,0.}; } LD spfa(int y,LD x){int i,j,u,v,head=0,tail;di[y][que[tail=1]=n]=0.;visit[n]=true;while(head!=tail){visit[u=que[head=head%len+1]]=false;for (i=point[u];i;i=next[i]){if (di[y][v=edge[i].en]>di[y][u]+edge[i].va-x*edge[i].si){di[y][v]=di[y][u]+edge[i].va-x*edge[i].si;if (!visit[v]) visit[que[tail=tail%len+1]=v]=true;}}} } bool judge(int x,LD y){int i=cnt[y];if (!i) spfa(i=cnt[y]=++tt,y);return cmp(di[i][x],0.)<=0; } LD isap(){int i,j,u,v,minn;LD mn,ans=0.;bool f;gap[0]=en-st+1;u=st;for (i=st;i<=en;++i) cur[i]=point[i];while(dis[st]<=en-st+1){f=false;for (i=cur[u];i;i=next[i])if (edge[i].va>eps&&dis[edge[i].en]+1==dis[u]){cur[u]=i;f=true;break;}if (f){pre[u=edge[i].en]=i;if (u==en){for (mn=inf,i=en;i!=st;i=edge[pre[i]].st)mn=min(mn,edge[pre[i]].va);ans+=mn;for (i=en;i!=st;i=edge[pre[i]].st){edge[pre[i]].va-=mn;edge[pre[i]^1].va+=mn;}u=st;}}else{if (!(--gap[dis[u]])) return ans;minn=en-st+1;for (cur[u]=i=point[u];i;i=next[i])if (edge[i].va>eps) minn=min(minn,dis[edge[i].en]);++gap[dis[u]=minn+1];if (u!=st) u=edge[pre[u]].st;}}return ans; } int main(){int m,i,j,n1,m1,u,v,vv;LD t,s,l,r,mid;scanf("%d%d",&n,&m);for (tot=0,i=1;i<=m;++i){scanf("%d%d%lf%lf",&u,&v,&t,&s);add(u,v,t,s);}memset(di,127,sizeof(di));spfa(cnt[++tt]=1,0.);tot=1;scanf("%d%d",&m1,&n1);for (i=1;i<=n1;++i) vi[i]=(cmp(di[1][i],di[0][0])<0);for (i=n1;i;--i){if (!vi[i]){dq[i]=inf;continue;}l=0.;r=10.;while(r-l>eps){mid=(l+r)/2.;if (judge(i,mid)) r=mid;else l=mid;}dq[i]=l;}memset(point,0,sizeof(point));st=0;en=n1+1;for (i=1;i<=n1;i+=2) add2(st,i,dq[i]);for (i=2;i<=n1;i+=2) add2(i,en,dq[i]);for (i=1;i<=m1;++i){scanf("%d%d",&u,&v);add2(u,v,inf);}t=isap();if (t<inf) printf("%.1f\n",t);else printf("-1\n"); }View Code
?
bzoj3597 方伯伯運椰子
題目大意;給出一個有向無環圖,已知每條路的參數ai、bi、ci、di,表示壓縮/擴容一流量的費用,當前流量和流過1流量的費用。求(X-Y)/k的最大值,其中X指改變前費用、Y是改變后的,k是改變次數,不能改變起點出發的邊,要求改變前后的總流量不變,且每條邊都滿流。
思路:0/1分數規劃+spfa判負環??紤]二分答案mid,如果(X-Y)-k*mid>0,就說明答案可以更大,變換一下就是(Y-X)+k*mid<0。對于給定的滿流的網絡,可以建圖:u->v:bi+di+mid的邊,表示擴容;如果c>0,說明可以壓縮,v->u:ai-di+mid的邊。從起點連出去的邊不能改變,所以只建u->v,0的邊就可以了。因為<0,所以找負環就可以了。
注意:1)不是找從起點到終點的負路徑,因為要滿足流量的要求,所以一定是一個環的某些邊+1,某些邊-1。
? 2)只對c>0的邊建反向邊,表示可以壓縮。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define N 5005 #define M 6005 #define LD double #define eps 1e-9 #define epe 1e-4 #define inf 1e50 #define len 100000 using namespace std; int in(){char ch=getchar();int x=0;while(ch<'0'||ch>'9') ch=getchar();while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x;} int cmp(LD x,LD y){if (x-y>eps) return 1;if (y-x>eps) return -1;return 0;} struct use{int u,v,ai,bi,ci,di;}ed[M]; int n,m,point[N],next[M],en[M],tot,que[len+1],vz[N]; LD va[M],dis[N]; bool vi[N]; void add(int i,LD x){int u,v;u=ed[i].u;v=ed[i].v;next[++tot]=point[u];point[u]=tot;en[tot]=v;va[tot]=(LD)(ed[i].bi+ed[i].di)+x;if (!ed[i].ci) return;next[++tot]=point[v];point[v]=tot;en[tot]=u;va[tot]=(LD)(ed[i].ai-ed[i].di)+x; } bool spfa(){int i,u,v,head=0,tail;for (i=n+2;i;--i){dis[i]=inf;vi[i]=false;vz[i]=0;}vi[que[tail=1]=n+1]=true;++vz[n+1];dis[n+1]=0.;while(head!=tail){vi[u=que[head=head%len+1]]=false;for (i=point[u];i;i=next[i]){if (cmp(dis[v=en[i]],dis[u]+va[i])>0){dis[v]=dis[u]+va[i];if (!vi[v]){vi[que[tail=tail%len+1]=v]=true;++vz[v];if (vz[v]>n) return true;}}}}return false; } bool judge(LD x){int i;tot=0;memset(point,0,sizeof(point));for (i=1;i<=m;++i){if (ed[i].u==n+1){next[++tot]=point[ed[i].u];point[ed[i].u]=tot;en[tot]=ed[i].v;va[tot]=0.;}else add(i,x);}return spfa(); } int main(){int i;LD l,r,mid;n=in();m=in();l=r=0.;for (i=1;i<=m;++i){ed[i]=(use){in(),in(),in(),in(),in(),in()};r=(LD)ed[i].di*(LD)ed[i].ci;}while(r-l>epe){mid=(l+r)/2.;if (judge(mid)) l=mid;else r=mid;}printf("%.2f\n",l); }View Code
?
轉載于:https://www.cnblogs.com/Rivendell/p/4915975.html
總結
- 上一篇: 电动车电瓶多少钱一个啊?
- 下一篇: Hat’s Words(字典树)