hdu4966 hdu4009 (最小树形图)

14 篇文章 0 订阅
2 篇文章 0 订阅

 

hdu4966

一开始以为是网络流,各种坑队友,后来才发现是相当裸的最小树形图。

 

添加一个根,连边到各课程的0级,权值为0。

然后每个课程从(i)级连边到(i-1)级,权值为0.

最后根据课程间的关系再连边。

 

 

#include <cstdio>
#include <cstring>
#include <iostream>

using namespace std;

const int maxn = 555, maxm = 500 * 2000, inf = 1e9;

struct Edge{
    int u, v, w;
    Edge(){}
    Edge(int u, int v, int w):
    u(u), v(v), w(w){}
}e[maxm];
int ec;
int lev[55], begid[55], endid[55];
int N;

int mincost[maxn], pre[maxn];
bool makeMin(int root){
    for(int i = 1; i <= N; i++) mincost[i] = inf;
    for(int i = 0; i < ec; i++){
        if(e[i].u == e[i].v) continue;
        int v = e[i].v;
        if(mincost[v] > e[i].w){
            mincost[v] = e[i].w;
            pre[v] = e[i].u;
        }    
    }
    for(int i = 1; i <= N; i++){
        if(i == root) continue;
        if(mincost[i] == inf) return false;
    }
    return true;
}

int vis[maxn];
int id[maxn];
bool existCircle(int root, int &res){
    memset(vis, -1, sizeof(vis));
    memset(id, -1, sizeof(id));
    int ind = 1;
    mincost[root] = 0;
    for(int i = 1; i <= N; i++){
        int v = i;
        res += mincost[i];
        while(vis[v] != i && v != root && id[v] == -1) {
            vis[v] = i, v = pre[v];
        }
        if(v != root && id[v] == -1){
            for(int u = pre[v]; u != v; u = pre[u]) id[u] = ind;
            id[v] = ind++;
        }
    }
    if(ind == 1) return false;
    for(int i = 1; i <= N; i++)
        if(id[i] == -1) id[i] = ind++;
    N = ind - 1;
    return true;
}

void update(){
    for(int i = 0; i < ec; i++){
        int v = e[i].v;
        e[i].u = id[e[i].u];
        e[i].v = id[e[i].v];
        if(e[i].u == e[i].v) continue;
        e[i].w -= mincost[v];            
    }
}

int DMST(int root){
    int res = 0;
    while(1){
        if(!makeMin(root)) return -1;
        if(!existCircle(root, res)) return res;
        update();
        root = id[root];
    }
    return -1;
}

int main(){
//    freopen("in", "r", stdin);
//    freopen("out", "w", stdout);
    int n, m;
    while(scanf("%d%d", &n, &m), n&&m){
        for(int i = 1; i <= n; i++) scanf("%d", lev+i);    
        lev[0] = 1, begid[0] = 0;    
        for(int i = 1; i <= n; i++) begid[i] = begid[i-1] + lev[i-1] + 1;
        N = begid[n] + lev[n];        
        int c, l, d, r, w;
        int root = 1;
        ec = 0;
        for(int i = 1; i <= n; i++)
        e[ec++] = Edge(root, begid[i], 0);
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= lev[i]; j++){
                int u = begid[i] + j, v = u - 1;
                e[ec++] = Edge(u, v, 0);
            }
        }
        for(int i = 0; i < m; i++){
            scanf("%d%d%d%d%d", &c, &l, &d, &r, &w);
            int v = begid[d] + r;
            for(int j = l, u = begid[c] + l; j <= lev[c]; u++, j++){
                e[ec++] = Edge(u, v, w);    
            }
        }    
        int ans = DMST(root);
        if(~ans) printf("%d\n", ans);
        else puts("-1");
    }
    return 0;
}

 

 

 

hdu4009

一个村子有n个房子,每间房子的坐标为(x,y,z)

如果自己打井,则代价为 z * A

如果从别的房子那里连水管过来,则代价为 两点的曼哈顿距离*B; 如果源点的z比此地的z小,则代价要加上C

求让所有房子都有水的最小代价。(怎么样都能保证每个房子都有水,所以根本不会输出“poor XiaoA”...)

 

加入根,然后根到每个点的权值为该点自己打井的代价,

然后其他的根据输入建边。

一个优化是,如果某个点从其他点连的边代价比直接自己打井还大,那么就可以不连。

然后就是IO输入优化了。

 

 

#include <cstdio>
#include <cstring>
#include <iostream>

using namespace std;

const int maxn = 1002, maxm = 1002 * 1004, inf = 0x3f3f3f3f;
#define ABS(x) ( (x)>0? (x) : (-(x)) )
struct Edge{
    int u, v, w;
    Edge(){}
    Edge(int u, int v, int w):
    u(u), v(v), w(w){}
}e[maxm];
int ec;
int N;

int mincost[maxn], pre[maxn];
bool makeMin(int root){
    memset(mincost, 0x3f, sizeof(mincost));
    for(int i = 0; i < ec; i++){
        if(e[i].u == e[i].v) continue;
        int v = e[i].v;
        if(mincost[v] > e[i].w){
            mincost[v] = e[i].w;
            pre[v] = e[i].u;
        }    
    }
    for(int i = 1; i <= N; i++){
        if(i == root) continue;
        if(mincost[i] == inf) return false;
    }
    return true;
}

int vis[maxn];
int id[maxn];
bool existCircle(int root, int &res){
    memset(vis, -1, sizeof(vis));
    memset(id, -1, sizeof(id));
    int ind = 1;
    mincost[root] = 0;
    for(int i = 1; i <= N; i++){
        int v = i;
        res += mincost[i];
        while(vis[v] != i && v != root && id[v] == -1) {
            vis[v] = i, v = pre[v];
        }
        if(v != root && id[v] == -1){
            for(int u = pre[v]; u != v; u = pre[u]) id[u] = ind;
            id[v] = ind++;
        }
    }
    if(ind == 1) return false;
    for(int i = 1; i <= N; i++)
        if(id[i] == -1) id[i] = ind++;
    N = ind - 1;
    return true;
}

void update(){
    for(int i = 0; i < ec; i++){
        int v = e[i].v;
        e[i].u = id[e[i].u];
        e[i].v = id[e[i].v];
        if(e[i].u == e[i].v) continue;
        e[i].w -= mincost[v];            
    }
}

int DMST(int root){
    int res = 0;
    while(1){
        if(!makeMin(root)) return -1;
        if(!existCircle(root, res)) return res;
        update();
        root = id[root];
    }
    return -1;
}

struct Pos{
    int x, y, z;
}p[maxn];
inline int dis(const Pos& a, const Pos& b){
    return ABS(a.x-b.x) + ABS(a.y-b.y) + ABS(a.z-b.z);    
}

inline int nextInt(){
    char ch = ' ';
    int res = 0;
    while(!isdigit(ch)) ch = getchar();
    while(isdigit(ch)){
        res *= 10;
        res += ch - '0';
        ch = getchar();
    }
    return res;
}
int self[maxn];
int main(){
//    freopen("in.txt", "r", stdin);
//    freopen("out", "w", stdout);
    int n, x, y, z;
    while(n = nextInt(), x = nextInt(), y = nextInt(), z = nextInt(), n && x && y && z){
        ec = 0;
        N = n + 1;
        for(int i = 1; i <= n; i++) {
         p[i].x = nextInt();
         p[i].y = nextInt();
         p[i].z = nextInt();
    }
        for(int i = 1; i <= n; i++){
            self[i] = p[i].z * x;
            e[ec++] = Edge(N, i, self[i]);
       }
        for(int i = 1; i <= n; i++){
            int k;
        k = nextInt();
            for(int j = 0, v, w; j < k; j++){
                v = nextInt();
                if(v == i) continue;
                w = dis(p[i], p[v]);
                w *= y;
        if(p[i].z < p[v].z)
                    w += z;
            if(w >= self[v]) continue;    
                e[ec++] = Edge(i, v, w);
            }
        }
        printf("%d\n", DMST(N));
    }
    return 0;
}

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值