关于邻接矩阵的拆点 和一些杂七杂八想不到的做法

下午遇到了 LuoguP3597LuoguP4159

这应该是我在网络流后第二次遇到的拆点。这两道题是结合邻接矩阵和拆点。

邻接矩阵有一个性质:设邻接矩阵A,则在矩阵Ak中,点aij的值表示从点i到j长度为k的通路数量。长度表示边的个数。

P4159 要求的是:在有向图中,从起点到终点的路径权值和为k的路径数。

题目里,点的个数为n<=10,边的权值为1<=w<=9;

由于边的权值w很小,所以可以拆点,是边的权值为1,分为多条边。这样子就可以用邻接矩阵表示整个一阶有向图的连通关系。

所以从起点到终点 路径权值和为k的路径数,等效于,从起点到终点长度为k的通路数。

所以,拆点后需要求的是,在k阶邻接矩阵中A[st][ed]的值。k很大没关系,矩阵快速幂即可。

至于怎么拆点,P4159的题解写的很清楚。

·设整个有向图的边权值最大值为w,把每个点拆成w个点;

·令有序对(i,j)   (i∈[1,n]∩Z,j∈[0,w-1]∩Z)表示点 i 拆成的第 j 个点,其中第 0 个点是“真”点,其余的是“假”点;

·令(i,j)  (j∈[1,w-1]∩Z) 向 (i,j-1)连一条边权为 1的边;

·对于原图 若 u到v有一条权值为w‘的边 则令(u,0)到(v,w‘-1)连一条权值为1的边。



对于P3597

要求的是k短路,每个点每条边可以重复经过。

一开始想到的是19年ccpc网络赛的1004,那时要求的也是k短路,但是那时k正常大,用的是单调堆栈,这道题不行。k<=1e18。

然后, 边权:1<=w<=3!

所以还是邻接矩阵。

但不同上面那题,给出长度求出个数,这次是给出个数求长度。有点麻烦。

很麻烦。

因为我不会。

所以对着唯一的一篇题解琢磨了很久。

题解有一个巧妙的做法,无法说出它为什么对,但动手模拟了一下,它就是对的。

先这样:

·设初始矩阵为 f

·令f[0][0]=1;注:题目 对于顶点u 1<=u<=n,即u!=0;

·对于所有1<=i<=n,令f[i][0]=1;

之后,跟普通的拆边一样连点。

这样做的结果是:

对于k阶矩阵f^k 的每一个fk[i][0]  表示的是:以i为起点,长度小于k的 通路的数量+1

即 num=fk[i][0]-1  :以点i为起点 长度小于k的通路数有num条。

之后是倍增记录矩阵的状态,然后通过计算每个矩阵 当前 长度内  的通路数量 来 判断长度所在的区间,然后再去处理细节。

倍增真的是一个很巧妙的做法啊。

#include<bits/stdc++.h>
#define debug printf("!");
#define mp make_pair
using namespace std;
typedef long long ll;
const int maxn=5e3+50;
const int inf=0x3f3f3f3f;

const int N=155;

struct P{
    ll a[N][N];
}f[70];

void mul(ll a[][N],ll b[][N],int n,ll res[][N])
{
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
        {
            res[i][j]=0;
            for(int k=0;k<n;k++)res[i][j]+=a[i][k]*b[k][j];
        }
}
ll cal(ll a[][N],int n,ll k)
{
    ll res=0;
    for(int i=1;i<=n;i++)
    {
        res+=a[i][0]-1;
        if(k<res)return res;
    }
    return res;
}
void print(ll a[][N],int n)
{
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<n;j++)printf("%d ",a[i][j]);
        putchar(10);
    }
}
void copy(ll a[][N],ll b[][N],int n)
{
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)a[i][j]=b[i][j];
}
int main()
{
    int n,m,i,u,v,w;ll k;
    scanf("%d%d%lld",&n,&m,&k);
    f[0].a[0][0]=1;
    for(i=1;i<=n;i++)
    {
        f[0].a[i][0]=1;
        f[0].a[i+n][i]=1;
        f[0].a[i+n+n][i+n]=1;
        /*
         使得所有的点都连向0点
         之后统计f^k 的a[i][0] 表示的是 以i点为起点 长度在k之内的边的个数+1
         至于为什么,手动 画图,和邻接矩阵 就懂了...
        */
    }
    while(m--)
    {
        scanf("%d%d%d",&u,&v,&w);
        f[0].a[u][v+(w-1)*n]++;
    }

    ll temp[N][N],t[N][N],ans=0,c;
    for(i=0;i<3*n+1;i++)t[i][i]=1;
    for(i=1;i<=64;i++)
    {
        mul(f[i-1].a,f[i-1].a,3*n+1,f[i].a);
        if(cal(f[i].a,n,k)>=k)break;
    }
    if(i==65)
    {
        puts("-1");return 0;
    }
    for(;i>=0;i--)
    {
        mul(t,f[i].a,3*n+1,temp);
        c=cal(temp,n,k);
        if(c<=k)
        {
            copy(t,temp,3*n+1);
            ans+=1ll<<i;
        }
        if(c==k)break;
    }
    printf("%lld\n",c==k?ans-1:ans);
}

原文地址:https://www.cnblogs.com/kkkek/p/11828021.html

时间: 2024-07-29 13:22:20

关于邻接矩阵的拆点 和一些杂七杂八想不到的做法的相关文章

【LeetCode】138. Copy List with Random Pointer

题目: A linked list is given such that each node contains an additional random pointer which could point to any node in the list or null. Return a deep copy of the list. 提示: 此题有两种方法,一种是按照原链表next的顺序依次创建节点,并处理好新链表的next指针,同时把原节点与新节点的对应关系保存到一个hash_map中,然后第

退役前的做题记录3

[CERC2017]Gambling Guide 设 \(f_u\) 表示 \(u\) 到 \(n\) 的期望. \(f_n=0\) \[f_u=1+\sum_{v\in suf_v}\frac{min(f_u,f_v)}{d_u}\] \[\rightarrow f_u=1+\sum_{v\in suf_u,f_v<f_u}\frac{f_v}{d_u}+\sum_{v\in suf_u,f_v\ge f_u}\frac{f_u}{d_u}\] \[\rightarrow f_u=\sum_{

POJ 1815 - Friendship - [拆点最大流求最小点割集][暴力枚举求升序割点] - [Dinic算法模板 - 邻接矩阵型]

妖怪题目,做到现在:2017/8/19 - 1:41-- 不过想想还是值得的,至少邻接矩阵型的Dinic算法模板get√ 题目链接:http://poj.org/problem?id=1815 Time Limit: 2000MS Memory Limit: 20000K Description In modern society, each person has his own friends. Since all the people are very busy, they communic

hdu 4289 网络流拆点,类似最小割(可做模板)邻接矩阵实现

Control Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 2247    Accepted Submission(s): 940 Problem Description You, the head of Department of Security, recently received a top-secret informatio

POJ 3281 网络流(dinic邻接矩阵、单路增广、多路增广)

思路:刚开始看题就想到怎么建图了,源点连向所有的食物,食物连牛,牛连饮料,饮料连汇点,所有的流量都是1.不过这样建图好后,WA了.原来是一头牛只能单一匹配一组食物和饮料,所以牛得拆点,牛之间得相连,流量为1,以保证单一匹配食物和饮料. 邻接矩阵dinic单路的代码: #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<map> #in

深入剖析Java中的装箱和拆箱

阅读目录 一.什么是装箱?什么是拆箱?二.装箱和拆箱是如何实现的三.面试中相关的问题 自动装箱和拆箱问题是Java中一个老生常谈的问题了,今天我们就来一些看一下装箱和拆箱中的若干问题.本文先讲述装箱和拆箱最基本的东西,再来看一下面试笔试中经常遇到的与装箱.拆箱相关的问题. 回到顶部 一.什么是装箱?什么是拆箱? 我们知道 Java为每种基本数据类型都提供了对应的包装器类型,至于为什么会为每种基本数据类型提供包装器类型在此不进行阐述,有兴趣的朋友可以查阅相关资料.在Java SE5之前,如果要生成

HDU——1874畅通工程续(邻接矩阵弗洛伊德)

畅通工程续 Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 41849    Accepted Submission(s): 15463 Problem Description 某省自从实行了很多年的畅通工程计划后,终于修建了很多路.不过路多了也不好,每次要从一个城镇到另一个城镇时,都有许多种道路方案可以选择,而某些方案要比另一些方案行走

hdu4725 拆点+最短路

题意:有 n 个点,每个点有它所在的层数,最多有 n 层,相邻两层之间的点可以互相到达,消耗 c (但同一层并不能直接到达),然后还有一些额外的路径,可以在两点间互相到达,并且消耗一定费用.问 1 点到 n 点的最小花费 将每一层拆成两个点,分别为进入层和出发层,然后相邻层的出发层可以指向进入层,花费 c,每个点可以到达其出发层,而进入层可以到达该点,花费 0 ,最后建立其余双向边,最短路 1 #include<stdio.h> 2 #include<string.h> 3 #in

hdoj 4289 Control 【拆点 求最小割】

Control Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 2295    Accepted Submission(s): 961 Problem Description You, the head of Department of Security, recently received a top-secret informati