[BZOJ 1407][NOI 2002]Savage(中国剩余定理+扩展欧几里得)

142 篇文章 0 订阅
31 篇文章 0 订阅

题目链接

http://www.lydsy.com/JudgeOnline/problem.php?id=1407

思路

由于此题里 n  的范围很小,因此可以直接从小到大暴力枚举洞穴个数m (注:此题并不满足二分的性质),那么问题转变为一个判定性问题:已知 m  ,问所有野人在它们的有生之年里是否会出现冲突。直接暴力枚举野人i  j  ,我们要求他们俩第一次相遇的时间x ,容易构造一个同余式

(step i step j )xpos j pos i (modm) 

我们要求的就是这个同余式的最小解 x min  
可以转换为下面的式子
(step i step j )x+my=pos j pos i  

由于上面的 x,y  的系数并不是两两互质,因此需要先对所有的系数和常数除以 gcd  。可以通过扩展欧几里得找到一个 (x 0 ,y 0 )  ,显然
step i step j gcd(step i step j ,m) x 0 +mgcd(step i step j ,m) y 0 =pos j pos i gcd(step i step j ,m)  

显然一个可行解是 (gcd(step i step j ,m)x 0 ,gcd(step i step j ,m)y 0 ) 
x  的通解满足x=gcd(step i step j ,m)x 0 +kmgcd(step i step j ,m)  ,若最小的解 x min min{age i ,age j }  则表明当前的 m  不合法,若枚举完所有的点对(i,j) 都合法,则表明当前的解 m  <script id="MathJax-Element-819" type="math/tex">m</script>是合法的。

代码

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>

#define MAXN 80

using namespace std;

struct Node
{
    int val; //键值
    int weight; //权值
    int frequence; //频率
}node[MAXN];

int n,K;

bool cmp(Node a,Node b)
{
    return a.val<b.val;
}

int f[MAXN][MAXN][MAXN],stack[MAXN],top=0; //边界:f[i][i-1][w]=0,表示点i为根结点,下面无左儿子(i-1为根节点,下面无右儿子),显然f值为0
int sum[MAXN]; //频率的前缀和

int main()
{
    scanf("%d%d",&n,&K);
    for(int i=1;i<=n;i++)
        scanf("%d",&node[i].val);
    for(int i=1;i<=n;i++)
        scanf("%d",&node[i].weight),stack[++top]=node[i].weight;
    for(int i=1;i<=n;i++)
        scanf("%d",&node[i].frequence);
    sort(stack+1,stack+top+1);
    for(int i=1;i<=n;i++)
        node[i].weight=lower_bound(stack+1,stack+top+1,node[i].weight)-stack;
    sort(node+1,node+n+1,cmp);
    for(int i=1;i<=n;i++)
        sum[i]=sum[i-1]+node[i].frequence;
    memset(f,0x3f,sizeof(f));
    for(int i=1;i<=n+1;i++)
        for(int w=0;w<=n;w++)
            f[i][i-1][w]=0;
    for(int w=n;w>=1;w--)
        for(int i=n;i>=1;i--)
            for(int j=i;j<=n;j++)
                for(int k=i;k<=j;k++) //枚举[i,j]这段区间的点对应的子树,该子树以点k作为根节点
                {
                    f[i][j][w]=min(f[i][j][w],f[i][k-1][w]+f[k+1][j][w]+K+sum[j]-sum[i-1]); //k本来权值小于K,将其权值变为k
                    if(node[k].weight>=w) f[i][j][w]=min(f[i][j][w],f[i][k-1][node[k].weight]+f[k+1][j][node[k].weight]+sum[j]-sum[i-1]); //k的权值本身就大于等于K,因此可以不变
                }
    int ans=0x3f3f3f3f;
    for(int w=0;w<=n;w++)
        ans=min(ans,f[1][n][w]);
    printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值