2014湘潭全國邀請賽I題 Intervals /POJ 3680 / 在限制次數下取有權區間使權最大/小問題(費用流)

來源:互聯網
上載者:User

標籤: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;}






聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.