poj 1149题解

题目来源:http://poj.org/problem?id=1149

题目大意:

Mirko在猪场工作,有N个猪圈,每个猪圈有pig[i]个猪,但是他不能打开任何猪圈的门,有M个人按顺序来买猪,每个人有K个猪圈的钥匙,有一个最大的可能要买的数量buy[i],但是Mirko 可以卖给他任意数量的猪(当然不能超过MAX),这个人走后Mikro可以重新分配这K个猪圈里的猪,然后猪圈又会把门锁起来,问你最终,Mikro可以买最多的猪的数量。

题目分析:

这道题是一道经典的构图题,题的难点在于怎么构图,然后就是简单的最大流的问题了。

构图原则及分析:

1.首先源点设为0,汇点设为M+1,M个人设为网络流中其他的点,N个猪圈什么都不算,只用在构图上!

2.假设第i个猪圈有cnt[i]个人有钥匙,并且这cnt[i]个人的编号分别是key[1]<key[2]<.....<key[cnt[i]],如果当前源点和key1之间没有边,那么连一条源点到key1的边,容量为pig[key1],如果当前源点和key1之间有边,则在这条边上容量加上pig[i];然后在key[i]和key[i+1](1<=i<cnt[i])之间连一条容量为INF的边。

3.每个卖家i(1<=i<=M)到汇点之间连一条容量为buy[i]的边.。

构图完毕!

可是为什么这样构图呢?可以简单的分析一下:

首先对于某一个猪圈i,如果第j个人是第一个有这个猪圈的钥匙的卖家,那么对于总的交易量来说可能增加的最大数目为pig[i],因为之前所有的人都没有这个猪圈的钥匙,所以这个猪圈的猪从没有被卖过或者调换过。而且这个增量是由第j个人实现的,所以效果就是源点和他的边的容量加上pig[i]。

但是这并没有包含Mikro可以“使坏”的操作,即猪圈之间的调换,假设第i个人有p,q猪圈的钥匙,第i+1个人有p,r的钥匙,那么是不是意味着第i+1个人可以买p,q,r三个猪圈的所有的猪,也就是说第i个人能买的第i+1个都能买!所以i和i+1这条边的容量应该是无限大的!

然后就是实现的问题了,我用的dinic

代码实现:


#define MAXPIG 1010
#include <cstdio>
#include <cstring>
#include <climits>
#define INF 99999999
using namespace std;
int N,M;

const int MAXN=110,MAXM=440000;
int pig[MAXPIG];
int key[MAXPIG][MAXN];
int cnt[MAXPIG];
int buy[MAXN];
int Sbuyer[MAXN];
struct Dinic
{
    struct edge
    {
        int x,y;//两个顶点
        int c;//容量
        int f;//当前流量
        edge *next,*back;//下一条边,反向边
        edge(int x,int y,int c,edge* next):x(x),y(y),c(c),f(0),next(next),back(0) {}
        void* operator new(size_t, void *p)
        {
            return p;
        }
    }*E[MAXN],*data;//E[i]保存顶点i的边表
    char storage[2*MAXM*sizeof(edge)];
    int S,T;//源、汇

    int Q[MAXN];//DFS用到的queue
    int D[MAXN];//距离标号,-1表示不可达
    void DFS()
    {
        memset(D,-1,sizeof(D));
        int head=0,tail=0;
        Q[tail++]=S;
        D[S]=0;
        for(;;)
        {
            int i=Q[head++];
            for(edge* e=E[i]; e; e=e->next)
            {
                if(e->c==0)continue;
                int j=e->y;
                if(D[j]==-1)
                {
                    D[j]=D[i]+1;
                    Q[tail++]=j;
                    if(j==T)return;
                }
            }
            if(head==tail)break;
        }
    }
    edge* cur[MAXN];//当前弧
    edge* path[MAXN];//当前找到的增广路
    int flow()
    {
        int res=0;//结果,即总流量
        int path_n;//path的大小
        for(;;)
        {
            DFS();
            if(D[T]==-1)break;
            memcpy(cur,E,sizeof(E));
            path_n=0;
            int i=S;
            for(;;)
            {
                if(i==T) //已找到一条增广路,增广之
                {
                    int mink=0;
                    int delta=INT_MAX;
                    for(int k=0; k<path_n; ++k)
                    {
                        if(path[k]->c < delta)
                        {
                            delta = path[k]->c;
                            mink=k;
                        }
                    }
                    for(int k=0; k<path_n; ++k)
                    {
                        path[k]->c -= delta;
                        path[k]->back->c += delta;
                    }
                    path_n=mink;//回退
                    i=path[path_n]->x;
                    res+=delta;
                }
                edge* e;
                for(e=cur[i]; e; e=e->next)
                {
                    if(e->c==0)continue;
                    int j=e->y;
                    if(D[i]+1==D[j])break;//找到一条弧,加到路径里
                }
                cur[i]=e;//当前弧结构,访问过的不能增广的弧不会再访问
                if(e)
                {
                    path[path_n++]=e;
                    i=e->y;
                }
                else //该节点已没有任何可增广的弧,从图中删去,回退一步
                {
                    D[i]=-1;
                    if(path_n==0)break;
                    path_n--;
                    i=path[path_n]->x;
                }
            }
        }
        return res;
    }
    int cut(int* s)
    {
        int rst=0;
        for(int i=0; i<MAXN; ++i)
            if(D[i]==-1&&E[i])
                s[rst++]=i;
        return rst;
    }
    void init(int _S,int _T)
    {
        S=_S,T=_T;
        data=(edge*)storage;
        memset(E,0,sizeof(E));
    }
    void add_edge(int x,int y,int w) //加进一条x至y容量为w的边,需要保证0<=x,y<MAXN,0<w<=INT_MAX
    {
        E[x]=new((void*)data++) edge(x,y,w,E[x]);
        E[y]=new((void*)data++) edge(y,x,0,E[y]);
        E[x]->back = E[y];
        E[y]->back = E[x];
    }
};

Dinic dinic;

int main()
{
    scanf("%d%d",&N,&M);
    int rst=0;
    int S=0,T=M+1;
    char c;
    dinic.init(S,T);
    memset(cnt,0,sizeof(cnt));
    memset(Sbuyer,0,sizeof(Sbuyer));
    for(int i=1; i<=N; ++i)
    {
        scanf("%d",&pig[i]);
    }
    int K;
    for(int i=1;i<=M;i++)
    {
        scanf("%d",&K);
        int x;
        for(int j=1;j<=K;j++)
        {
            scanf("%d",&x);
            key[x][cnt[x]++] = i;
        }
        scanf("%d",&buy[i]);
    }
    for(int i=1;i<=N;i++)
    {
        if(cnt[i] > 0)
        Sbuyer[key[i][0]] += pig[i];
        //printf("%d ",Sbuyer[key[i][0]]);
    }
    for(int i=1;i<=M;i++)
    {
        dinic.add_edge(i,T,buy[i]);
        //printf("%d ",Sbuyer[i]);
        if(Sbuyer[i]>0)
        dinic.add_edge(S,i,Sbuyer[i]);
    }
    for(int i=1;i<=N;i++)
    {
        for(int j=0;j<cnt[i]-1;j++)
        {
            //printf("%d %d\n",key[i][j],key[i][j+1]);
            dinic.add_edge(key[i][j],key[i][j+1],INF);
        }
    }
    rst=dinic.flow();
    printf("%d\n",rst);
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
POJ3635是一道经典的数学题,需要使用一些数学知识和算法进行解决。 题目描述: 给定四个正整数 a、b、p 和 k,求 a^b^p mod k 的值。 解题思路: 首先,我们可以将指数 b^p 写成二进制形式:b^p = c0 * 2^0 + c1 * 2^1 + c2 * 2^2 + ... + ck * 2^k,其中 ci 为二进制数的第 i 位。 然后,我们可以通过快速幂算法来计算 a^(2^i) mod k 的值。具体来说,我们可以用一个变量 x 来存储 a^(2^i) mod k 的值,然后每次将 i 加 1,如果 ci 为 1,则将 x 乘上 a^(2^i) mod k,最后得到 a^b^p mod k 的值。 代码实现: 以下是 Java 的代码实现: import java.util.*; import java.math.*; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); BigInteger a = sc.nextBigInteger(); BigInteger b = sc.nextBigInteger(); BigInteger p = sc.nextBigInteger(); BigInteger k = sc.nextBigInteger(); BigInteger ans = BigInteger.ONE; for (int i = 0; i < p.bitLength(); i++) { if (b.testBit(i)) { ans = ans.multiply(a.modPow(BigInteger.ONE.shiftLeft(i), k)).mod(k); } } System.out.println(ans); } } 其中,bitLength() 函数用于获取二进制数的位数,testBit() 函数用于判断二进制数的第 i 位是否为 1,modPow() 函数用于计算 a^(2^i) mod k 的值,multiply() 函数用于计算两个 BigInteger 对象的乘积,mod() 函数用于计算模数。 时间复杂度: 快速幂算法的时间复杂度为 O(log b^p),其中 b^p 为指数。由于 b^p 的位数不超过 32,因此时间复杂度为 O(log 32) = O(1)。 总结: POJ3635 是一道经典的数学题,需要使用快速幂算法来求解。在实现时,需要注意 BigInteger 类的使用方法,以及快速幂算法的细节。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值