Lightoj 1380 1384 最小树形图

两道最小树形图。所谓最小树形图指的是有向图的最小生成树。

参考了多位大神的博文终于自己写出来了。。

 

有向图的最小生成树指的是在给定的有向图上找到一个子图

点集与原图相同。边集是原边集的子集。

有定根的生成树指的是规定某个点为生成树的根,向外延边扩展形成生成树。

 

算法名字叫做 刘朱算法。复杂度是O(VE)

参考博客 http://www.cppblog.com/RyanWang/archive/2010/01/25/106427.aspx

 

1380是很明显的最小树形图题目,直接对给定有向图求树形图即可。

模版题

实现起来还是有很多要注意的细节。。

#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>
#include <cstdio>
#include <stack>
#include <cstring>
using namespace std;
  
struct edge{
    int p,l;
    edge(int p,int l):p(p),l(l){}
};
  
const int MAXN = 1010;
const int INF = 0x64646464;
vector <edge> e[MAXN<<1];
typedef vector<edge>::iterator iter;
int T,N,M,K,cas=0;
int p1,p2,l;
  
int prev[MAXN<<1];
int minc[MAXN<<1];
bool del[MAXN<<1];
int vis[MAXN<<1];
bool wor[MAXN<<1];
int value[MAXN<<1];
int ne[MAXN<<1];
int all;
stack <int> s;
  
int dfs(int p){
    int rul=1;
    vis[p]=true;
    for (iter k=e[p].begin();k!=e[p].end();k++)
        if (!vis[k->p]) rul+=dfs(k->p);
    return rul;
}
  
bool dfs2(int p,int index){
    if (p==K) return false;
    if (vis[p]==index) return true;
    vis[p]=index;
    s.push(p);
    if (dfs2(prev[p],index)) return true;
    s.pop();
    return false;
}
  
void merge(){
    memset(ne,100,sizeof(ne));
    memset(wor,0,sizeof(wor));
    int P = prev[s.top()];
    int now = -1;
  
    while (now!=P){
        now = s.top();
        wor[now]=true;
        s.pop();
        for (iter k=e[now].begin();k!=e[now].end();k++)
            ne[k->p]=min(ne[k->p],k->l);
    }
//out 向外的边
    for (int i=0;i<all;i++){
        if (del[i]) continue;
        if (wor[i]) continue;
        if (ne[i]==INF) continue;
        e[all].push_back(edge(i,ne[i]));
    }
//in 向内的边
    for (int i=0;i<all;i++){
        int l = INF;
        if (del[i]) continue;
        if (wor[i]) continue;
        for (iter k=e[i].begin();k!=e[i].end();k++){
            if (!wor[k->p]) continue;
            l=min(l,k->l-minc[k->p]);
        }
        if (l==INF) continue;
        e[i].push_back(edge(all,l));
    }
  
    for (int i=0;i<all;i++)
        if (wor[i]){
            del[i]=true;
            value[all]+=minc[i];
            value[all]+=value[i]; //缩点时要加上原来的点的权
        }
    all++;
}
  
int main(){
    freopen("in.txt","r",stdin);
    scanf("%d",&T);
    while (T--){
        memset(del,0,sizeof(del));
        memset(value,0,sizeof(value));
        for (int i=0;i<(MAXN<<1);i++) e[i].clear();
        memset(vis,0,sizeof(vis));
  
        scanf("%d%d%d",&N,&M,&K);
  
        for (int i=1;i<=M;i++){
            scanf("%d%d%d",&p1,&p2,&l);
            e[p1].push_back(edge(p2,l));
        }
        if (dfs(K)!=N){
            printf("Case %d: impossible\n",++cas);
            continue;
        }
        all=N;
        bool flag=false;
        while(!flag){
            memset(minc,100,sizeof(minc));
            memset(vis,-1,sizeof(vis));
            for (int i=0;i<all;i++){
                if (del[i]) continue;
                for (iter k=e[i].begin();k!=e[i].end();k++){
                    if (k->l<minc[k->p]){
                        minc[k->p]=k->l;
                        prev[k->p]=i;
                    }
                }
            }
            flag=true;
            for (int i=0;i<all;i++){
                if (del[i]) continue;
                if (vis[i]!=-1) continue;
                if (dfs2(i,i)) {
                    flag=false;
                    merge();
                    break;
                }
            }
        }
        int ans=0;
        for (int i=0;i<all;i++){
            if (i==K) continue;
            if (del[i]) continue;
            ans+=minc[i];
            ans+=value[i];
        }
        printf("Case %d: %d\n",++cas,ans);
    }
}
View Code

 

1384则多了一个限制条件。。在规定的费用内使得带宽最大。

二分最大的带宽,然后在不考虑小于当前带宽的边的情况下计算最小树形图的权值

(如果不存在则返回最大)

权值小于规定的费用则当前带宽合法,否则不合法。。

复杂度为 (VE*log(m))

直接修改1380的代码,加上二分即可。

 

注意初始化问题,在上次计算后可能有新的边在邻接表中,

将编号大于等于N的边全部删除即可

#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>
#include <cstdio>
#include <stack>
#include <cstring>
using namespace std;
 
struct edge{
    int p,l,c;
    edge(int p,int l,int c):p(p),l(l),c(c){}
};
 
const int MAXN = 60;
const int INF = 0x64646464;
vector <edge> e[MAXN<<1];
typedef vector<edge>::iterator iter;
int T,N,M,K,C,cas=0;
int p1,p2,l,c;
int mid;
 
int prev[MAXN<<1];
int minc[MAXN<<1];
bool del[MAXN<<1];
int vis[MAXN<<1];
bool wor[MAXN<<1];
int value[MAXN<<1];
int ne[MAXN<<1];
int cc[10010];
int all;
stack <int> s;
 
int dfs(int p){
    int rul=1;
    vis[p]=true;
    for (iter k=e[p].begin();k!=e[p].end();k++)
        if (k->c>=cc[mid] && !vis[k->p]) rul+=dfs(k->p);
    return rul;
}
 
bool dfs2(int p,int index){
    if (p==K) return false;
    if (vis[p]==index) return true;
    vis[p]=index;
    s.push(p);
    if (dfs2(prev[p],index)) return true;
    s.pop();
    return false;
}
 
void merge(){
    memset(ne,100,sizeof(ne));
    memset(wor,0,sizeof(wor));
    int P = prev[s.top()];
    int now = -1;
 
    while (now!=P){
        now = s.top();
        wor[now]=true;
        s.pop();
        for (iter k=e[now].begin();k!=e[now].end();k++){
            if (k->c<cc[mid]) continue;
            ne[k->p]=min(ne[k->p],k->l);
        }
    }
//out
    for (int i=0;i<all;i++){
        if (del[i]) continue;
        if (wor[i]) continue;
        if (ne[i]==INF) continue;
        e[all].push_back(edge(i,ne[i],cc[mid]));
    }
//in
    for (int i=0;i<all;i++){
        int l = INF;
        if (del[i]) continue;
        if (wor[i]) continue;
        for (iter k=e[i].begin();k!=e[i].end();k++){
            if (!wor[k->p]) continue;
            if (k->c<cc[mid]) continue;
            l=min(l,k->l-minc[k->p]);
        }
        if (l==INF) continue;
        e[i].push_back(edge(all,l,cc[mid]));
    }
 
    for (int i=0;i<all;i++)
        if (wor[i]){
            del[i]=true;
            value[all]+=minc[i];
            value[all]+=value[i];
        }
    all++;
}
 
int check(){
    memset(del,0,sizeof(del));
    memset(value,0,sizeof(value));
    for (int i=0;i<N;i++)
        while (!e[i].empty() && e[i].back().p>=N) e[i].pop_back();
    for (int i=N;i<=all;i++) e[i].clear();
    memset(vis,0,sizeof(vis));
 
    if (dfs(K)!=N) return INF;
    all=N;
    bool flag=false;
    while(!flag){
        memset(minc,100,sizeof(minc));
        memset(vis,-1,sizeof(vis));
        for (int i=0;i<all;i++){
            if (del[i]) continue;
            for (iter k=e[i].begin();k!=e[i].end();k++){
                if (k->c<cc[mid]) continue;
                if (k->l<minc[k->p]){
                    minc[k->p]=k->l;
                    prev[k->p]=i;
                }
            }
        }
        flag=true;
        for (int i=0;i<all;i++){
            if (del[i]) continue;
            if (vis[i]!=-1) continue;
            if (dfs2(i,i)) {
                flag=false;
                merge();
                break;
            }
        }
    }
    int ans=0;
    for (int i=0;i<all;i++){
        if (i==K) continue;
        if (del[i]) continue;
        ans+=minc[i];
        ans+=value[i];
    }
    return ans;
}
 
int main(){
    freopen("in.txt","r",stdin);
    scanf("%d",&T);
    while (T--){
        scanf("%d%d%d",&N,&M,&C);
        for (int i=0;i<(MAXN<<1);i++) e[i].clear();
        for (int i=1;i<=M;i++){
            scanf("%d%d%d%d",&p1,&p2,&c,&l);
            e[p1].push_back(edge(p2,l,c));
            cc[i]=c;
        }
        sort(cc+1,cc+M+1);
        int r = unique(cc+1,cc+M+1)-cc-1;
        int l = 1;
        int ans=-1;
        while (l<=r){
            mid = (l+r)>>1;
            int now = check();
            if (now<=C) {
                ans = mid;
                l=mid+1;
            } else r=mid-1;
        }
        if (ans==-1) printf("Case %d: impossible\n",++cas);
            else printf("Case %d: %d kbps\n",++cas,cc[ans]);
    }
}
View Code

 

 

posted @ 2013-09-24 13:14  qinhang3  阅读(265)  评论(1编辑  收藏  举报