01分数规划
bzoj1486 最小圈
題目大意:求一個(gè)圖內(nèi)的某個(gè)環(huán),使得sigma ai[i]/k(環(huán)上點(diǎn)數(shù))最小。
思路:二分答案,如果sigma ai[i]-k*mid>0說明mid可以更大,每次判斷的時(shí)候給所有邊-mid,就成了判斷負(fù)環(huán)的問題。這里用spfa普通的判法會(huì)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 圈地游戲
題目大意:給定一個(gè)網(wǎng)格,格內(nèi)和邊上都有權(quán)值,求一個(gè)圈,使得格內(nèi)的權(quán)值和v/邊上的權(quán)值和c最大。
思路:二分答案k,判斷能否存在v-ck>0,就是求所有格內(nèi)權(quán)值-不選的格的權(quán)值-邊界上選的格在邊上的權(quán)值-選的兩個(gè)格間的邊權(quán),然后就是總的格內(nèi)的價(jià)值-最小割,S表示選,T表示不選,那么從S向邊界的點(diǎn)連邊界的邊權(quán),相鄰兩點(diǎn)連中間邊權(quán),每個(gè)點(diǎn)向T連格內(nèi)邊權(quán)。
#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個(gè)點(diǎn)和m條有向邊(有時(shí)間ti和安全系數(shù)si),點(diǎn)是兩排點(diǎn),前n1個(gè)點(diǎn)間有一些連接兩排點(diǎn)的空腔,一條路徑的安全度是sigma(ti)/sigma(si),如果一只軍隊(duì)能從n到一個(gè)點(diǎn),就能訪問過所有這個(gè)點(diǎn)連出去的空腔,求訪問過所有空腔的最小的安全度和。
思路:分?jǐn)?shù)規(guī)劃+網(wǎng)絡(luò)流。對(duì)于每個(gè)有空腔連接的點(diǎn),分?jǐn)?shù)規(guī)劃求出從n到每一個(gè)點(diǎn)的最小安全度(二分答案mid,最短路,sigma(ti)-midsigma(si)<0,可以用map存一下每一種二分的mid,算過就不用算了,這樣可以節(jié)省計(jì)算量)。然后就是從兩排點(diǎn)中選出一些點(diǎn)覆蓋所有的空腔且權(quán)值和最小,這是最小點(diǎn)權(quán)覆蓋的模型,從s向第一排點(diǎn)連邊權(quán)值,從第二排向終點(diǎn)連邊權(quán)值,將空腔所連接的兩個(gè)點(diǎn)連邊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 方伯伯運(yùn)椰子
題目大意;給出一個(gè)有向無環(huán)圖,已知每條路的參數(shù)ai、bi、ci、di,表示壓縮/擴(kuò)容一流量的費(fèi)用,當(dāng)前流量和流過1流量的費(fèi)用。求(X-Y)/k的最大值,其中X指改變前費(fèi)用、Y是改變后的,k是改變次數(shù),不能改變起點(diǎn)出發(fā)的邊,要求改變前后的總流量不變,且每條邊都滿流。
思路:0/1分?jǐn)?shù)規(guī)劃+spfa判負(fù)環(huán)。考慮二分答案mid,如果(X-Y)-k*mid>0,就說明答案可以更大,變換一下就是(Y-X)+k*mid<0。對(duì)于給定的滿流的網(wǎng)絡(luò),可以建圖:u->v:bi+di+mid的邊,表示擴(kuò)容;如果c>0,說明可以壓縮,v->u:ai-di+mid的邊。從起點(diǎn)連出去的邊不能改變,所以只建u->v,0的邊就可以了。因?yàn)?lt;0,所以找負(fù)環(huán)就可以了。
注意:1)不是找從起點(diǎn)到終點(diǎn)的負(fù)路徑,因?yàn)橐獫M足流量的要求,所以一定是一個(gè)環(huán)的某些邊+1,某些邊-1。
? 2)只對(duì)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
?
轉(zhuǎn)載于:https://www.cnblogs.com/Rivendell/p/4915975.html
總結(jié)
- 上一篇: 电动车电瓶多少钱一个啊?
- 下一篇: Hat’s Words(字典树)