LUOGU 2792: [JSOI2008] store shopping minimal tree

title

LUOGU 2792

Simplify the meaning of the questions:

A plurality of items, each item has a List, an item purchase other items can be purchased in a more competitive prices. Everything has a number of requirements, you can not buy more, and no less to buy (0 if demand can not buy, even if it may make the best total price).

analysis

This problem has a great greedy algorithm, for an item, how do we make it cost a minimum purchase it?

We can greedy take the smallest items in this all possible options (with the original price) inside , do a multiplication to answer.

The key is how to make this greed is correct? We found that if all the items are purchased , then you can guarantee that greed is correct.

Then we can put this problem into two questions:

  1. Each item is determined to buy a minimum cost and;

  2. After all items are purchased one, we calculate the cost of greed and the remaining items;

So how to solve the problem 1?

First, we offer a program for every \ ((A, b, the p-) \) ,

  • If the item \ (a \) is not required: from then we \ (a \) to connect \ (b \) a right to a value \ (inf \) side. Want a good reason, since the goods \ (a \) can not be selected, then select items \ (a \) preferential schemes are certainly illegal, the right value is set to \ (inf \) to ensure that this program will not be selected to;
  • If the item \ (b \) is not required: then we from A A to \ (b \) and even a right to a value \ (0 \) side. Since the article \ (b \) is not required, we can be seen as purchase items \ (b \) does not require any cost;
  • If you need to, it would be normal from \ (a \) to \ (b \) and even a right to a value \ (p \) side.

So we finished processing discount program.

For the original price, we create a new virtual node, and from all points of the virtual node to connect an edge, if necessary, the right value of the original item, otherwise \ (0 \) .

This picture is a virtual root node of a directed graph, and then we found this problem there are several beautiful nature:

  1. There has to FIG root;
  2. All the points have been selected to;
  3. Right side and minimum;

So, it is the smallest tree, and run it again just fine.

code

#include<bits/stdc++.h>

const int maxn=64,maxm=maxn*maxn;
const double inf=1e9;

namespace IO
{
    char buf[1<<15],*fs,*ft;
    inline char getc() { return (ft==fs&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),ft==fs))?0:*fs++; }
    template<typename T>inline void read(T &x)
    {
        x=0;
        T f=1, ch=getchar();
        while (!isdigit(ch) && ch^'-') ch=getchar();
        if (ch=='-') f=-1, ch=getchar();
        while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
        x*=f;
    }
}

using IO::read;

template<typename T>inline bool chkMin(T &a,const T &b) { return a>b ? (a=b, true) : false; }
template<typename T>inline bool chkMax(T &a,const T &b) { return a<b ? (a=b, true) : false; }

int n,m,r;
double ans;
struct Orz{int x,y; double z;}e[maxm];
namespace Edmonds
{
    int pre[maxn];//fa[y] 当前连到 y 点的最短边的 x
    double In[maxn];//In[x] 为当前连到 x 点的最短边的边权
    int vis[maxn];//vis[x] 代表 x 所在链的代表元素,类似并查集
    int id[maxn];//id[x] 代表 x 节点在第 id[x] 个环中
    inline void zhu_liu()
    {
        while (1)
        {
            for (int i=1; i<=n; ++i) In[i]=inf;
            for (int i=1,x,y; i<=m; ++i)
            {
                x=e[i].x, y=e[i].y;
                if (x^y && e[i].z<In[y]) pre[y]=x, In[y]=e[i].z;//遍历所有边,对每个点找到最小的入边
            }

            int cnt=0;//cnt 代表当前图环的数量
            for (int i=1; i<=n; ++i) vis[i]=id[i]=0;
            for (int i=1; i<=n; ++i)
            {
                if (i==r) continue;
                ans+=In[i];
                int x=i;
                for ( ; x^r && vis[x]^i && !id[x]; x=pre[x]) vis[x]=i;//找环
                if (x^r && !id[x])
                {
                    id[x]=++cnt;//把环上的点标记为同一点
                    for (int y=pre[x]; y^x; y=pre[y]) id[y]=cnt;
                }
            }
            if (!cnt) break;//无环,得到解
            for (int i=1; i<=n; ++i)
                if (!id[i]) id[i]=++cnt;

            for (int i=1,x,y; i<=m; ++i)//建立新图,缩点,重新标记
            {
                x=e[i].x, y=e[i].y;
                if ( (e[i].x=id[x])^(e[i].y=id[y]) ) e[i].z-=In[y];//修改边权
            }
            n=cnt,r=id[r];
        }
    }
}

using Edmonds::zhu_liu;

double c[maxn];
int need[maxn],tot;
int main()
{
    read(n);
    for (int i=1; i<=n; ++i)
    {
        scanf("%lf",&c[i]), read(need[i]);
        e[++tot]=(Orz){n+1,i,!need[i] ? 0 : c[i]};
    }

    read(m);
    for (int i=1,a,b; i<=m; ++i)
    {
        read(a);read(b);
        double p;scanf("%lf",&p);
        if (!need[a]) e[++tot]=(Orz){a,b,inf};
        else if (!need[b]) e[++tot]=(Orz){a,b,0};
        else chkMin(c[b],p), e[++tot]=(Orz){a,b,p};
    }

    for (int i=1; i<=n; ++i) ans+=(need[i]-1)*c[i];
    r=n+1, m+=n, ++n;
    zhu_liu();
    printf("%.2lf\n",ans);
    return 0;
}

Guess you like

Origin www.cnblogs.com/G-hsm/p/11407617.html