FYN OI奋斗之路11~

XJOI 奋斗群群赛17解题报告

原题

https://vjudge.net/contest/189941 (Codeforces Beta Round #11)

A. Increasing Sequence

题意

给定一个序列,有一种操作可以使序列中某一个数加上d,问最少进行多少次操作使得该序列递增。

题解

对于每一个数,求出进行几次操作能使其大于上一个数并将其更新,最后将总操作次数相加。

#include<bits/stdc++.h>
using namespace std;
const int MAXN=2010;
int b[MAXN];
int main(){
    int n,d;
    int ans=0;
    scanf("%d %d",&n,&d);
    for(int i=1;i<=n;i++){
        scanf("%d",&b[i]);
        if(i>1&&b[i]<=b[i-1]){
            int need=(b[i-1]-b[i])/d+1;
            b[i]=b[i]+need*d;
            ans+=need;
        }
    }
    printf("%d",ans);
}

B. Jumping Jack

题意

一个人要从0处跳到k处,他第一次跳的距离为1,之后每一次跳的距离加1,跳的方向可以往左或往右,问跳几次可以刚好到达k处

题解

以k为正数为例,向左跳相当于距离减了两倍的跳动距离,因此求出向右跳几次能使距离-k为偶数且距离>k即可,至于具体哪一步是左跳,哪一步是右跳我们无需考虑。

#include<bits/stdc++.h>
using namespace std;
typedef long long int LL;
const int MAXN=1001000;
int main() {
    int tmp;
    LL now=0;
    LL x;
    scanf("%lld",&x);
    if(x==0) {
        printf("0");
        return 0;
    }
    if(x>0){
    for(int i=1; i<=MAXN; i++) {
        now+=i;
        if(now>=x) {
            //cout<<now<<endl;
            tmp=i;
            break;
        }
    }
    if(now==x) printf("%d",tmp);
    else if((now-x)%2==0)printf("%d",tmp);
    else {
        while((now-x)%2!=0){
            tmp++;
            now+=tmp;
        }
        printf("%d",tmp);
    }
    }
    if(x<0){
        for(int i=1; i<=MAXN; i++) {
        now-=i;
        if(now<=x) {
            tmp=i;
            break;
        }
    }
    if(now==x) printf("%d",tmp);
    else if((now-x)%2==0)printf("%d",tmp);
    else {
        while((now-x)%2!=0){
            tmp++;
            now+=tmp;
        }
        printf("%d",tmp);
    }
    } 
}

C-How Many Squares?

题意

给出一个由0,1组成的n*m大小的矩阵,问四边都是数字1的正方形和菱形的数量。

题解

首先对图进行处理,遍历每一个1的点,并对这个点周围的8个点进行扩展(这里用了向量实现),因为我们的遍历有方向性,所以遍历到有效的点时,将它的值改为2,即该点已经遍历。之后计数正方形与菱形。

#include<bits/stdc++.h>
using namespace std;
int dic[8][2]= {{0,1},{1,0},{0,-1},{-1,0},{1,1},{1,-1},{-1,1},{-1,-1}};
char a[255][255];
int len=0;
int n,m;
void judge(int x,int y) {
    if(x<0||x>=n||y<0||y>=m||a[x][y]!='1')return ;
    a[x][y]='2';
    len++;
    for(int i=0; i<8; i++) {
        judge(x+dic[i][0],y+dic[i][1]);
    }
}
bool solve(int x,int y,int len,int l,int r) {
    for(int i=0; i<len+1; i++)
        for(int j=l; j<r; j++) {
            if(x+dic[j][0]*i<0||x+dic[j][0]*i>=n||y+dic[j][1]*i<0||y+dic[j][1]*i>=m||a[x+dic[j][0]*i][y+dic[j][1]*i]!='2')
                return 0;
        }
    return 1;
}
int main() {
    int T;
    scanf("%d",&T);
    while(T--) {
        scanf("%d%d",&n,&m);
        for(int i=0; i<n; i++)
            scanf("%s",a[i]);
        int res=0;
        for(int i=0; i<n; i++)
            for(int j=0; j<m; j++) {
                if(a[i][j]=='1') {
                    len=0;
                    judge(i,j);
                    if(len%4)continue;
                    res+=solve(i,j,len/4,0,2)&&solve(i+len/4,j+len/4,len/4,2,4);
                    res+=solve(i,j,len/4,4,6)&&solve(i+len/2,j,len/4,6,8);
                }
            }
        printf("%d\n",res);
    }
    return 0;
}

D. A Simple Task

题意

给出一副无向图,求环的个数。

题解

刚拿到题目,还以为是用并查集判断环,于是一直没打出来,后来一问原来是状态压缩dp。
为什么用到的是状态压缩dp呢?首先我们注意到点数并不是很多,而题目的路径状态有点类似于TSP问题,因此想到可以dp,二维dp[i][j]表示路径状态为i,路径结尾为j,由于是环,我们定义一个环的结尾为该环中标号最小的点,这样就不会重复计算了,最后由于每个环会算两遍所以答案除以2。

#include<bits/stdc++.h>
using namespace std;
typedef long long int LL;
LL f[1<<20][20],t=0;
int g[20][20],i,j,k,x,y,n,m,q;
int main() {
    scanf("%d %d",&n,&m);
    while(m--) {
        scanf("%d %d",&x,&y);
        x--,y--;
        g[x][y]=g[y][x]=1;
        if(x>y)swap(x,y);
        f[(1<<x)+(1<<y)][y]++;
    }
    for(i=0; i<(1<<n); i++) {
        q=1;
        for(j=0; j<n; j++)if(((i&(1<<j)))&&(!f[i][j]))if(q)q=0,k=j;
                else {
                    for(y=k+1; y<n; y++)if((i&(1<<y))&&(g[y][j]))f[i][j]+=f[i-(1<<j)][y];
                    if(g[k][j])t+=f[i][j];
                }
    }
    printf("%lld\n",t/2);
}

总结

思维方面还是欠缺,导致D题一直没想出标算,dp题其实很锻炼思维,需要多加练习,加油!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值