標籤:blog http 使用 os 2014 io
先說POJ3680:給n個有權(權<10w)開區間(n<200),(區間最多數到10w)保證數軸上所有數最多被覆蓋k次的情況下要求總權最大,輸出最大權。
思路: 限制的處理:s-->開始流量為k,要求總權最大,即費用最大,所以費用取負,最小費用最大流即可。對於輸入區間[a,b]:w,添加邊:a-->b,流量為1,費用為-w。
對於點i,i+1,添加邊,費用為0,流量無窮。顯然這種處理,限制了區間最多取k次,(流量控制),跑最大流能走添加的邊盡量走,且越大越好(負數剛剛是最小費用),滿足題意。但是TLE,因為w到10W,邊10W,必然逾時 ,所以點要處理, 所有點“壓縮”,向前推進,只要存在的點,前一個向後一個連邊即可,詳見代碼。
再看湘潭的這題: 問題相反:給n個有權(權<10w)開區間(n<2000),(區間最多數到10的9次),保證區間【1-,m】最少被覆蓋k次的情況下要求總權最小,輸出最小權。
思路:(感激zz1215的建圖提示) 限制的處理:顯然在出口處流量必需達到k才算有解。對於輸入區間[a,b]:w,添加邊:a-->b,流量為1,費用為w,但是這樣處理,點都是離散的,根本沒有體現連續性,
不可能像上題那樣建圖(否則費用為0),所以:這樣:點I向它前一個點連邊,費用為0,流量為無窮,這樣巧妙的解決了離散點問題。跑最小費用即可。顯然,之前要處理點。
#include<iostream>#include<queue>#include<cstdio>using namespace std;const int inf=0x3f3f3f3f;const int t=100000;int n,k;int e[300001][4];int head[100101];int nume=0;void inline adde(int from,int to,int w,int c){ e[nume][0]=to;e[nume][1]=head[from];head[from]=nume; e[nume][2]=w;e[nume++][3]=c; e[nume][0]=from;e[nume][1]=head[to];head[to]=nume; e[nume][2]=-w;e[nume++][3]=0;}int inq[111005];int d[110000]; //spfabool spfa(int & sumcost) //每次求費用{ int pre[110005]; int minf=inf; int prv[110005]; for(int i=0;i<=t+1;i++) { inq[i]=0;d[i]=inf; } pre[0]=-1 ; prv[0]=-1; //路徑中,分別記錄到點i的邊,和i之前的點。(這題如果用矩陣建圖要方便) queue<int>q; q.push(0);inq[0]=1;d[0]=0; while(!q.empty()) { int cur=q.front(); q.pop(); inq[cur]=0; for(int i=head[cur];i!=-1;i=e[i][1]) { int v=e[i][0]; if(e[i][3]>0&&e[i][2]+d[cur]<d[v]) { d[v]=e[i][2]+d[cur]; prv[v]=cur; //記錄增廣路 pre[v]=i; if(!inq[v]) { q.push(v); inq[v]=1; } } } } if(d[t+1]==inf)return 0; //無法增廣 int cur=t+1; //目的點 while(cur!=0) //取路徑上最小殘流量邊作為流量增廣 { minf=e[pre[cur]][3]<minf?e[pre[cur]][3]:minf; cur=prv[cur]; } cur=t+1; while(cur!=0) //增廣,改流量 { e[pre[cur]][3]-=minf; e[pre[cur]^1][3]+=minf; cur=prv[cur]; } sumcost+=d[t+1]*minf; //費用為單位費用(該路徑下每條邊單位流量之和)*流量 return 1;}void mincost(int & sumcost){ while(spfa(sumcost)) ; //無法增廣為止 return ;}int hash[100011];void clear(){ nume=0; for(int i=0;i<=t+1;i++) { hash[i]=head[i]=-1; }}struct qujian{ int a,b,w;};int main(){ int T; scanf("%d",&T); for(int ii=1;ii<=T;ii++) { clear(); cin>>n>>k; int a,b,w; vector<qujian>qq(n); vector<int>v; for(int i=0;i<n;i++) { scanf("%d%d%d",&a,&b,&w); hash[a]=hash[b]=1; qq[i].a=a;qq[i].b=b;qq[i].w=w; adde(a,b,-w,1); } for(int i=0;i<100010;i++) //處理“存在”的點 if(hash[i]==1) { v.push_back(i); } for(int i=0;i<v.size()-1;i++) //“存在”的點連邊 { adde(v[i],v[i+1],0,k); } adde(v[v.size()-1],t,0,k); //超級源匯點的邊 adde(0,v[0],0,k);adde(t,t+1,0,k); int sumcost=0; mincost(sumcost); cout<<-sumcost<<endl; //相反數 } return 0;}
湘潭:
#include<iostream>#include<queue>#include<algorithm>using namespace std;const int inf=0x3f3f3f3f;int n,k,m;int countv=0;int f[4009];void getf(int x) // 把點1到10的9次(最多4000個點),壓縮到4000以內,一一對應,以便建圖。{ if(x>m){f[countv]=x;return ;} //大於m的數沒用,相當於m。 countv++; f[countv]=x;}int getv(int x) //擷取對應點{ if(x>=m){return countv;} for(int i=1;i<=countv;i++) { if(f[i]==x)return i; }}int e[20001][4];int head[4005];int nume=0;void inline adde(int from,int to,int w,int c){ e[nume][0]=to;e[nume][1]=head[from];head[from]=nume; e[nume][2]=w;e[nume++][3]=c; e[nume][0]=from;e[nume][1]=head[to];head[to]=nume; e[nume][2]=-w;e[nume++][3]=0;}int inq[4005];int d[4005]; //spfabool spfa(int & sumcost,int &sumflow) //每次求費用{ int pre[4005]; int minf=inf; int prv[4005]; for(int i=0;i<=countv+4;i++) { inq[i]=0; d[i]=inf; } pre[0]=-1 ; prv[0]=-1; //路徑中,分別記錄到點i的邊,和i之前的點。(這題如果用矩陣建圖要方便) queue<int>q; q.push(0);inq[0]=1;d[0]=0; while(!q.empty()) { int cur=q.front(); q.pop(); inq[cur]=0; for(int i=head[cur];i!=-1;i=e[i][1]) { int v=e[i][0]; if(e[i][3]>0&&e[i][2]+d[cur]<d[v]) { d[v]=e[i][2]+d[cur]; prv[v]=cur; //記錄增廣路 pre[v]=i; if(!inq[v]) { q.push(v); inq[v]=1; } } } } if(d[countv+1]==inf)return 0; //無法增廣 int cur=countv+1; //目的點 while(cur!=0) //取路徑上最小殘流量邊作為流量增廣 { minf=e[pre[cur]][3]<minf?e[pre[cur]][3]:minf; cur=prv[cur]; } cur=countv+1; while(cur!=0) //增廣,改流量 { e[pre[cur]][3]-=minf; e[pre[cur]^1][3]+=minf; cur=prv[cur]; } sumcost+=d[countv+1]*minf; //費用為單位費用(該路徑下每條邊單位流量之和)*流量 sumflow+=minf; return 1;}void mincost(int & sumcost,int & sumflow){ while(spfa(sumcost,sumflow)) ; //無法增廣為止 return ;}void clear(){ nume=countv=0; for(int i=0;i<=4003;i++) { head[i]=-1; f[i]=0; }}struct qujian{ int a,b,w;};int main(){ int T; cin>>T; for(int ii=1;ii<=T;ii++) { clear(); cin>>n>>k>>m; int a,b,w; vector<int>v; vector<qujian>qq(n); for(int i=0;i<n;i++) { cin>>a>>b>>w; v.push_back(a); v.push_back(b); qq[i].a=a;qq[i].b=b;qq[i].w=w; } sort(v.begin(),v.end()); //從小到大處理點 for(int i=0;i<v.size();i++) { getf(v[i]); } for(int i=0;i<n;i++) { int t1=getv(qq[i].a); int t2=getv(qq[i].b); adde(t1,t2,qq[i].w,1); //注意點:若使用adde(getv(a),getv(b),w,1)參數是從右往左開始賦值傳遞的!!! } for(int i=1;i<countv;i++) { adde(i+1,i,0,inf); //注意點:若使用adde(getv(a),getv(b),w,1)參數是從右往左開始賦值傳遞的!!! } adde(0,1,0,inf);adde(countv,countv+1,0,k); int sumcost=0; int sumflow=0; mincost(sumcost,sumflow); cout<<"Case "<<ii<<": "; if(sumflow!=k) //到不了k,無解 { cout<<-1<<endl; } else { cout<<sumcost<<endl; } } return 0;}