[NOI2014]随机数生成器

题意

  给定一个初始随机种子和一个随机数生成器,同时给出若干个交换对,求出交换后的矩阵路径序列排序后字典序最小的合法路径

题解

  一开始的处理很简单,直接按照题目的要求进行模拟,然后将矩阵生成即可,关键是后面的寻找路径过程
  容易想到,1肯定是要被选进去的,因为是要求将路径序列排序后的字典序最小;然后贪心地从小到大选择,判断是否能够加入当前序列,知道序列长度达到要求停止
  现在的问题就是怎么判断决策点能否加入当前序列。考虑对于每一行维护合法的区间(初始为 [1,m] [ 1 , m ] ),设决策点为 (xy) ( x , y ) ,如果 l[x]<=y<=r[x] l [ x ] <= y <= r [ x ] ,那么当前点就可以加入到序列中,然后就要更新矩阵的合法区间
  对于 i[xpre,x] i ∈ [ x p r e , x ] xpre x p r e 为决策点之前那一个点的横坐标),只需将 r[i] r [ i ] 更新为 y y 即可,对于i[x,xnext],只需将 l[i] l [ i ] 更新为 y y <script type="math/tex" id="MathJax-Element-12">y</script>即可

复杂度

O(跑得过)

代码

#include<iostream>
#include<cstdlib>
#include<cstdio>
#define Rint register int
#define Lint long long int
using namespace std;
const int N=5010;
int l[N],r[N],T[N*N],Tn[N*N];
int n,m,q;
Lint a,b,c,d,x_0;
int main()
{
    int u,v,x;
    scanf("%lld%lld%lld%lld%lld",&x_0,&a,&b,&c,&d);
    scanf("%d%d%d",&n,&m,&q);
    for(int i=1;i<=n*m;i++)   T[i]=i;
    for(int i=1;i<=n*m;i++)
    {
        x=(a*x_0*x_0+b*x_0+c)%d;
        swap( T[i],T[x%i+1] );
        x_0=x;
    }
    for(int i=1;i<=q;i++)
    {
        scanf("%d%d",&u,&v);
        swap( T[u],T[v] );
    }
    for(int i=1;i<=n;i++)   for(int j=1;j<=m;j++)   x=T[(i-1)*m+j],Tn[x]=(i-1)*m+j;
    for(int i=1;i<=n;i++)   l[i]=1,r[i]=m;
    for(int i=1;i<=n*m;i++)
    {
        u=Tn[i]%m ? Tn[i]/m+1:Tn[i]/m,v=Tn[i]%m ? Tn[i]%m:m;
        if( v<l[u] || v>r[u] )   continue ;
        for(int x=u-1;x>=1;x--)
            if( v<r[x] )   r[x]=v;
            else   break ;
        for(int x=u+1;x<=n;x++)
            if( v>l[x] )   l[x]=v;
            else   break ;
        printf("%d ",i);
    }
    printf("\n");
    return 0;
}
P2375 [NOI2014] 动物园是一道经典的动态规划题目,以下是该题的详细题意和解题思路。 【题意描述】 有两个长度为 $n$ 的整数序列 $a$ 和 $b$,你需要从这两个序列中各选出一些数,使得这些数构成一个新的序列 $c$。其中,$c$ 序列中的元素必须在原序列中严格递增。每个元素都有一个价值,你的任务是选出的元素的总价值最大。 【解题思路】 这是一道经典的动态规划题目,可以采用记忆化搜索的方法解决,也可以采用递推的方法解决。 记忆化搜索的代码如下: ```c++ #include <iostream> #include <cstdio> #include <cstring> using namespace std; const int MAXN = 1005; int dp[MAXN][MAXN], a[MAXN], b[MAXN], n; int dfs(int x, int y) { if (dp[x][y] != -1) return dp[x][y]; if (x == n || y == n) return 0; int res = max(dfs(x + 1, y), dfs(x + 1, y + 1)); if (a[x] > b[y]) { res = max(res, dfs(x, y + 1) + b[y]); } return dp[x][y] = res; } int main() { scanf("%d", &n); for (int i = 0; i < n; i++) scanf("%d", &a[i]); for (int i = 0; i < n; i++) scanf("%d", &b[i]); memset(dp, -1, sizeof(dp)); printf("%d\n", dfs(0, 0)); return 0; } ``` 其中,dp[i][j]表示选到a数组中第i个元素和b数组中第j个元素时的最大价值,-1表示未计算过。dfs(x,y)表示选到a数组中第x个元素和b数组中第y个元素时的最大价值,如果dp[x][y]已经计算过,则直接返回dp[x][y]的值。如果x==n或者y==n,表示已经遍历完一个数组,直接返回0。然后就是状态转移方程了,如果a[x] > b[y],则可以尝试选b[y],递归调用dfs(x, y+1)计算以后的最大价值。否则,只能继续遍历数组a,递归调用dfs(x+1, y)计算最大价值。最后,返回dp[0][0]的值即可。 递推的代码如下: ```c++ #include <iostream> #include <cstdio> #include <cstring> using namespace std; const int MAXN = 1005; int dp[MAXN][MAXN], a[MAXN], b[MAXN], n; int main() { scanf("%d", &n); for (int i = 0; i < n; i++) scanf("%d", &a[i]); for (int i = 0; i < n; i++) scanf("%d", &b[i]); for (int i = n - 1; i >= 0; i--) { for (int j = n - 1; j >= 0; j--) { dp[i][j] = max(dp[i + 1][j], dp[i + 1][j + 1]); if (a[i] > b[j]) { dp[i][j] = max(dp[i][j], dp[i][j + 1] + b[j]); } } } printf("%d\n", dp[0][0]); return 0; } ``` 其中,dp[i][j]表示选到a数组中第i个元素和b数组中第j个元素时的最大价值。从后往前遍历数组a和数组b,依次计算dp[i][j]的值。状态转移方程和记忆化搜索的方法是一样的。 【参考链接】 P2375 [NOI2014] 动物园:https://www.luogu.com.cn/problem/P2375
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值