poj 3680 Intervals(离散化+费用流)
題目鏈接:http://poj.org/problem?id=3680
題意:?給定n個帶權開區(qū)間,選擇其中一些區(qū)間出來,使得權值最大并且在任意被選區(qū)間的有效點上重疊層數(shù)不超過k。
解題思路:
這題可以用費用流解決,先講講如何建圖,再分析算法的正確性。
將所有區(qū)間的前后兩個端點離散化為n個不重復的點,然后建圖:
?源點s編號0, 區(qū)間端點編號1到n, 匯點t編號n+1.
?????? 從s到1號點有邊(s, 1, k, 0)
?????? 從i號節(jié)點到i+1號節(jié)點有邊(i, i+1, INF, 0)
?????? 從n號節(jié)點到t有邊(n, t, k, 0)
?????? 如果區(qū)間(a,b)的端點a和b分別是離散化后的第i和第j個點,那么有邊(i, j, 1, -w)注意:這里花費取原本區(qū)間權值的負數(shù),因為這樣我們最后求得的最小費用就是最大權值和的負數(shù).
最終算出的最小費用(負值),就是最大費用的相反數(shù)。
這里摘自一片牛人的博客:
?下面分析下該構圖為什么能得到解?
?????? 從源點流出了k個流量,那么這k個流量可以選擇從i到i+1這種普通邊流(因為該邊的容量無限大),但是如果此時在i到i+1之間有另外一條區(qū)間邊時(區(qū)間邊費用為負值), 由于我們求最小費用,所有k個流量中的一個肯定會沿著這條cost為負的區(qū)間邊流. 這是如果我們算最小費用,那么就會把該區(qū)間邊的最小費用算上去一次.
?????? 同理如果i到i+1點之間除了普通邊外,同時還有2條區(qū)間邊(區(qū)間邊cost都為負值),那么明顯k個流量肯定先分出兩個1的流量分別走這兩條區(qū)間邊,剩下的才去走那些個普通邊(因為普通邊cost為0,區(qū)間邊cost為負).
?????? 如果i到i+1除了普通邊1條外,還有8條權值不同的區(qū)間邊,且k=3,那么我們肯定是選權值最小的那3條區(qū)間邊去走,而不會去走另外權值大些的路.
這里的感覺就像是自來水管一樣,分流然后匯合,保證了管道內(nèi)的流量不超過k
#include<iostream> #include<cstdio> #include<cstring> #include<vector> #include<map> #include<queue> using namespace std;const int maxn = 405; const int inf = 0x3f3f3f3f; struct Edge {int from,to,flow,cost,next;Edge(){}Edge(int f,int t,int fl,int co):from(f),to(t),flow(fl),cost(co){} }; struct MCMF {int n,s,t;vector<Edge> edge;vector<int> G[maxn];int dis[maxn];int pre[maxn];bool inq[maxn];void init(int n,int s,int t){this->n = n, this->s = s, this->t = t;edge.clear();for(int i = 0; i <= n; i++) G[i].clear();}void addedge(int u,int v,int flow,int cost){edge.push_back(Edge(u,v,flow,cost));edge.push_back(Edge(v,u,0,-cost));int m = edge.size();G[u].push_back(m-2);G[v].push_back(m-1);}int spfa(){queue<int> q;memset(dis,inf,sizeof(dis));memset(pre,-1,sizeof(pre));memset(inq,false,sizeof(inq));dis[s] = 0;inq[s] = true;q.push(s);while(!q.empty()){int u = q.front();q.pop();inq[u] = false;for(int i = 0; i < G[u].size(); i++){int v = edge[G[u][i]].to;if(dis[v] > dis[u] + edge[G[u][i]].cost && edge[G[u][i]].flow > 0){dis[v] = dis[u] + edge[G[u][i]].cost;pre[v] = G[u][i];if(inq[v] == false){inq[v] = true;q.push(v);}}}}return dis[t] != inf;}int solve(){int mincost = 0,minflow;while(spfa()){minflow = inf;for(int i = pre[t]; i != -1; i = pre[edge[i].from])minflow = min(minflow,edge[i].flow);for(int i = pre[t]; i != -1; i = pre[edge[i].from]){edge[i].flow -= minflow;edge[i^1].flow += minflow;}mincost += dis[t] * minflow;}return mincost;} }MM;int x[maxn],y[maxn],w[maxn];//從1開始標號,記錄每個區(qū)間 int p[maxn];//離散化后的每個點 int num;//離散化去重后的點數(shù)目 int main() {int t;scanf("%d",&t);while(t--){int n,k; num = 0; scanf("%d%d",&n,&k); for(int i = 1; i <= n; ++i) { scanf("%d%d%d",&x[i],&y[i],&w[i]); p[num++] = x[i]; p[num++] = y[i]; } sort(p,p + num); num = unique(p,p + num) - p; map<int,int> mp;//坐標與編號的映射 for(int i = 0;i < num; ++i) mp[p[i]] = i + 1; int src=0, dst=num+1; MM.init(num+2,src,dst);MM.addedge(src,1,k,0); for(int i=1;i<=num;++i) MM.addedge(i,i+1,inf,0); for(int i=1;i<=n;++i) { MM.addedge(mp[x[i]],mp[y[i]],1,-w[i]); } printf("%d\n",-MM.solve());}return 0; }
總結
以上是生活随笔為你收集整理的poj 3680 Intervals(离散化+费用流)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 安装eclipse的JRebel6.4.
- 下一篇: MiniDao支持ID自增主键策略,使用