每日一练 19.2.20

18 篇文章 0 订阅

NOIP2014 普及组

比例简化(模拟)

题目描述
在社交媒体上,经常会看到针对某一个观点同意与否的民意调查以及结果。例如,对某一观点表示支持的有1498人,反对的有902人,那么赞同与反对的比例可以简单的记为1498:902。

不过,如果把调查结果就以这种方式呈现出来,大多数人肯定不会满意。因为这个比例的数值太大,难以一眼看出它们的关系。对于上面这个例子,如果把比例记为5:3,虽然与真实结果有一定的误差,但依然能够较为准确地反映调查结果,同时也显得比较直观。

现给出支持人数A,反对人数B,以及一个上限L,请你将A比B化简为A’比B’,要求在A’和B’均不大于L且A’和B’互质(两个整数的最大公约数是1)的前提下,A’/B’≥A/B且A’/B’-A/B的值尽可能小。

输入
输入共一行,包含三个整数A,B,L,每两个整数之间用一个空格隔开,分别表示支持人数、反对人数以及上限。1≤A≤1,000,000,1≤B≤1,000,000,1≤L≤100,A/B≤L。

输出
输出共一行,包含两个整数A’,B’,中间用一个空格隔开,表示化简后的比例。

样例输入
1498 902 10

样例输出
5 3

思路
枚举A’,B’,最符合条件的即为答案

代码实现

#include <iostream>
#include <cstdio>
#include <string>
#include <queue>
#include <cstring>
#include <cmath>
#include <cctype>
#include <vector>
#include <algorithm>
#include <map>
#include <stack>

using namespace std;
typedef long long ll;
typedef pair<int,int> pa;
const int N=100005;
const int mod=99999997;
int x,y,l;
int a,b;
int gcd(int x,int y)
{
    if(x<y) return gcd(y,x);
    if(y==0) return x;
    return gcd(y,x%y);
}
int main()
{
    scanf("%d%d%d",&x,&y,&l);
    double t1,t2,t3;
    t1=x*1.0/y;
    t3=l*1.0;
    for(int i=1;i<=l;i++)
    {
        for(int k=1;k<=l;k++)
        {
            t2=i*1.0/k;
            if(t2>=t1 &&gcd(i,k)==1 && t2-t1<t3)
            {
                a=i;
                b=k;
                t3=t2-t1;
            }
        }
    }
    printf("%d %d\n",a,b);
    return 0;
}

螺旋矩阵

题目描述
一个n行n列的螺旋矩阵可由如下方法生成:
从矩阵的左上角(第1行第1列)出发,初始时向右移动;如果前方是未曾经过的格子,则继续前进,否则右转;重复上述操作直至经过矩阵中所有格子。根据经过顺序,在格子中依次填入1,2,3,…,n2,便构成了一个螺旋矩阵。
下图是一个n=4时的螺旋矩阵。
在这里插入图片描述
现给出矩阵大小n以及i和j,请你求出该矩阵中第i行第j列的数是多少。

输入
输入共一行,包含三个整数 n,i,j,每两个整数之间用一个空格隔开,分别表示矩阵大小、待求的数所在的行号和列号。
1≤n≤30000,1≤i≤n,1≤j≤n

输出
输出共一行,包含一个整数,表示相应矩阵中第i行第j列的数。

样例输入
4 2 3

样例输出
14

思路:
将整个矩阵层层减去,找到最后点i,j为边的矩阵,易得到结果

代码实现

#include <iostream>
#include <cstdio>
#include <string>
#include <queue>
#include <cstring>
#include <cmath>
#include <cctype>
#include <vector>
#include <algorithm>
#include <map>
#include <stack>

using namespace std;
typedef long long ll;
typedef pair<int,int> pa;
const int N=100005;
const int mod=99999997;
int x,y,n;

int main()
{
    scanf("%d%d%d",&n,&x,&y);
    int sum=0;
    while(x!=n && y!=n && x>1 && y>1)
    {
        x--,y--;
        sum+=(4*n-4);
        n-=2;
    }
    if(x==1) sum+=y;
    else if(x>1 && x!=n)
    {
        if(y==1) sum+=(4*n-4-x+2);
        else sum+=(n-1+x);
    }
    else sum+=(3*n-1-y);
    printf("%d\n",sum);
    return 0;
}

子矩阵

题目描述
给出如下定义:
子矩阵:从一个矩阵当中选取某些行和某些列交叉位置所组成的新矩阵(保持行与列的相对顺序)被称为原矩阵的一个子矩阵。
例如,下面左图中选取第2、4行和第2、4、5列交叉位置的元素得到一个2*3的子矩阵如右图所示。

相邻的元素:矩阵中的某个元素与其上下左右四个元素(如果存在的话)是相邻的。
矩阵的分值:矩阵中每一对相邻元素之差的绝对值之和。
本题任务:给定一个n行m列的正整数矩阵,请你从这个矩阵中选出一个r行c列的子矩阵,使得这个子矩阵的分值最小,并输出这个分值。

输入
第一行包含用空格隔开的四个整数n,m,r,c,意义如问题描述中所述,每两个整数之间用一个空格隔开。
接下来的n行,每行包含m个用空格隔开的整数,用来表示问题描述中那个n行m列的矩阵。

输出
输出共1行,包含1个整数,表示满足题目描述的子矩阵的最小分值。

样例输入
5 5 2 3
9 3 3 3 9
9 4 8 7 4
1 7 4 6 6
6 8 5 6 9
7 4 5 6 1

样例输出
6

提示
该矩阵中分值最小的2行3列的子矩阵由原矩阵的第4行、第5行与第1列、第3列、第4列交叉位置的元素组成,为
6 5 6
7 5 6
,其分值为
|6−5| + |5−6| + |7−5| + |5−6| + |6−7| + |5−5| + |6−6| =6。
对于50%的数据,1 ≤ n ≤ 12,1 ≤ m ≤ 12,矩阵中的每个元素1 ≤ a[i][j] ≤ 20;
对于100%的数据,1 ≤ n ≤ 16,1 ≤ m ≤ 16,矩阵中的每个元素1 ≤ a[i][j] ≤ 1,000,1 ≤ r ≤ n,1 ≤ c ≤ m。

思路
通过枚举符合条件的行列组合,用DP的方法求解,得出最小值
详见大佬的博客
NOIP 普及组 T4 子矩阵(–洛谷P2258)

代码实现

#include <iostream>
#include <cstdio>
#include <string>
#include <queue>
#include <cstring>
#include <cmath>
#include <cctype>
#include <vector>
#include <algorithm>
#include <map>
#include <stack>

using namespace std;
typedef long long ll;
typedef pair<int,int> pa;
const int N=20;
const int Max=1e9;
int a[N][N],dp[N][N],R[N],cost[N][N],v[N];
int n,m,r,c,ans;
int judge()
{
    int ret=Max;
    for(int i=1;i<=m;i++)
    {
        v[i]=0;
        for(int k=1;k<r;k++) v[i]+=abs(a[R[k]][i]-a[R[k+1]][i]);
    }
    for(int i=1;i<=m;i++)
        for(int j=i+1;j<=m;j++)
        {
            cost[i][j]=0;
            for(int k=1;k<=r;k++) cost[i][j]+=abs(a[R[k]][i]-a[R[k]][j]);
        }
    for(int i=1;i<=m;i++)
    {
        for(int j=1;j<=i && j<=c;j++)
        {
            dp[i][j]=Max;
            for(int k=j-1;k<i;k++)
            {
                dp[i][j]=min(dp[i][j],dp[k][j-1]+cost[k][i]+v[i]);
            }
        }
    }
    for(int i=c;i<=m;i++) ret=min(ret,dp[i][c]);
    return ret;
}
void select(int num,int cnt)
{
    if(num>n)
    {
        if(cnt==r) ans=min(ans,judge());
        return ;
    }
    select(num+1,cnt);
    R[cnt+1]=num;
    select(num+1,cnt+1);
    return ;
}
int main()
{
    scanf("%d%d%d%d",&n,&m,&r,&c);
    for(int i=1;i<=n;i++)
        for(int k=1;k<=m;k++) scanf("%d",&a[i][k]);
    ans=Max;
    select(1,0);
    printf("%d\n",ans);
    return 0;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值