2015 Multi-University Training Contest 4--多校赛第四场--做完了再添加上来

1001>HDU 5327  Olympiad

题意:计算[L,R]区间中满足个十百千...等这些位上面的数字没有重复的数字的个数,比如11的个位和十位都是1,这是重复了,所以不满足条件,123个十百位分别是1,2,3,没有重复,所以满足条件

PS:别想太复杂,判断一个数是否满足条件就把每个位的数字取出来用hash表查看重复,当然我用深搜类似于数位DP的方法一个数一个数的拼接起来打表。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define Max(a,b) (a>b?a:b)
#define Min(a,b) (a<b?a:b)
using namespace std;
int s[111111],vis[11];
void dfs(int x,int l)//x记录当前拼接出来的数字,l用来区别是否可以添加0
{
    if(x>100000)return;
    s[x]=1;
    int i;
    for(i=l;i<10;i++)
    if(vis[i]==0)
    {
        vis[i]=1;
        dfs(x*10+i,0);//除了拼接数字,除了拼接首位,其他都可以添加0
        vis[i]=0;
    }
}
int main (void)
{
    int i,j,k,l,r,n;
    memset(s,0,sizeof(s));
    memset(vis,0,sizeof(vis));
    dfs(0,1);//从首位开始拼接
    for(i=1;i<=100000;i++)
    s[i]+=s[i-1];
    scanf("%d",&n);
    while(n--&&scanf("%d%d",&l,&r))
    {
        printf("%d\n",s[r]-s[l-1]);
    }
    return 0;
}

 

1002>HDU 5327 Problem Killer

题意:求输入序列的最长等差或等比的子序列,要求子序列是连续的

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define Max(a,b) (a>b?a:b)
#define Min(a,b) (a<b?a:b)
#define LL long long
using namespace std;
LL s[1111111];
int main (void)
{
    LL t,n,i,j,k,l,r,dz,dm,rl,dl,rf,df;
    scanf("%lld",&t);
    while(t--)
    {
        scanf("%lld",&n);
        r=dz=dm=-1;
        rf=df=0;
        rl=dl=l=1;
        for(i=0;i<n;i++)
        {
            scanf("%lld",&s[i]);
            if(i>0)//从第二个开始操作
            {
                if(rf==0)//是否第一次操作等差数列
                {
                    rf=1;
                    r=s[i]-s[i-1];//获取公差
                    rl=2;//标记长度
                }else
                {
                    k=s[i]-s[i-1];//获取公差
                    if(k==r)rl++;//查看是否公差一致
                    else
                    {
                        l=Max(l,rl);//不一致则标记最长长度
                        rl=2;//初始化长度
                        r=k;//初始化公差
                    }
                }
                if(df==0)是否第一次操作等比数列
                {
                    df=1;
                    dz=s[i];
                    dm=s[i-1];
                    dl=2;
                }
                else
                {
                    if(dz*s[i-1]==dm*s[i])dl++;//同上,但是这里最好用乘法,因为除法会有精度损失
                    else
                    {
                        l=Max(l,dl);
                        dl=2;
                        dz=s[i];
                        dm=s[i-1];
                    }
                }
            }
        }
        printf("%lld\n",Max(dl,Max(rl,l)));
    }
    return 0;
}

 

1009>HDU 5335 Walk Out

题意:给你个0/1矩阵,从左上角走到右下角得到一个二进制数,这个数由路径上的0/1组成,要你求得到的二进制数最小是多少。

PS:二进制数的大小首先由有效长度决定,越短越小,然后相同长度则是从高往低找第一个不同的数字,这个数字越小则二进制数越小,**前导零不算有效数字。

所以在这里可以从左上角开始找到一个或者一堆距离右下角最近的点,这是保证有效数字更少,然后从这些点往右往下找传递,在保证距离缩短的同时,当距离相同,有map的值等于0出现则舍弃map的值等于1的那条路,因为在相同长度上,这个位的数字1已经大于0了

***要用广搜,深搜会溢出,因为深搜不能及时释放内存

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#define Max(a,b) (a>b?a:b)
#define Min(a,b) (a<b?a:b)
#define LL long long
#define MAX 999999999
using namespace std;
int n,m,len;
char map[1111][1111];
int vis[1111][1111];
int dd[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
struct node
{
    int x,y;
}ss[111111];
int bfs()//用广搜来普及所有与(0,0)在一堆的map1=0的点,并且标记好
{
    int i,j,k,l,x,y,xx,yy,top,back;
    top=back=0;
    k=0;
    ss[top].x=ss[top].y=0;
    top++;
    while(top!=back)
    {
        x=ss[back].x;
        y=ss[back].y;
        back++;
        if(back>=111111)back=0;
        if(map[x][y]=='1')continue;
        for(i=0;i<4;i++)
        {
            xx=x+dd[i][0];
            yy=y+dd[i][1];
            if(xx>=0&&yy>=0&&xx<n&&yy<m&&vis[xx][yy]==0)
            {
                vis[xx][yy]=1;
                k=Max(k,xx+yy);
                ss[top].x=xx;
                ss[top].y=yy;
                top++;
                if(top>=111111)top=0;
            }
        }
    }
    return k;//返回的是距离(0,0)最远的点到(0,0)的距离,距离=x+y
}
int main (void)
{
    int t,i,j,k,l,f1,f2,x,y;
    scanf("%d",&t);
    while(t--&&scanf("%d%d%*c",&n,&m))
    {
        memset(vis,0,sizeof(vis));
        for(i=0;i<n;i++)
        gets(map[i]);
        vis[0][0]=1;
        k=bfs();
        if(k==n+m-2&&map[n-1][m-1]=='0')
        {//如果最大距离到达了右下角,而右下角的map是1,则直接特判
            puts("0");
            continue;
        }
        l=n+m-2;
        f1=1;//f1记录上一位操作中记录当前位的最优的数字
        for(;k<=l;k++)//用距离来表示二进制数的位
        {
            f2=1;//f2是标记当前位的最优数字
            for(x=0;x<n;x++)//遍历所有x
            {
                y=k-x;//用x求出y
                if(x>=0&&y>=0&&x<n&&y<m)//判断是否过界
                if(vis[x][y]==1&&map[x][y]-48<=f1)//判断是否是上一位用到了,并判断当前数字是否可取
                {//关于map[x][y]-48<=f1,如果当前位有map=0的出现,则f1=0,可以排除掉所有的map=1
//如果当前位没有map=0的出现,则f1=1,会将所有map=1选择
                    if(x<n-1)//往下走
                    {
                        vis[x+1][y]=1;//标记这个位置被用到
                        if(map[x+1][y]=='0')f2=0;//标记是否有0可取
                    }
                    if(y<m-1)/往右走
                    {
                        vis[x][y+1]=1;
                        if(map[x][y+1]=='0')f2=0;
                    }
                }
            }
            printf("%d",f1);//每操作完一位就输出一位
            f1=f2;//把当前标记递给f1
        }
        printf("\n");
    }
    return 0;
}

 

1010>HDU 5336  XYZ and Drops

题意:就是十滴水的游戏,水滴在大于4会分裂,给你r X c的地图,n个初始水滴,还有限制时间t,最后会给一个坐标(x,y)表示游戏开始时这个点默认为水滴分裂,要你求出t时间过后n个水滴的状态,存在就输出 "1 水滴大小",不存在就输出 "0 分裂时间"

PS: 这题不算难,是简化版的十滴水游戏,要注意的是分裂后产生的水滴只会在碰到初始水滴(即输入的n个位置上的水滴)才会停下来并融入,多个水滴同时融入一个初始水滴会叠加后分裂,比如两个水滴同时进入一个大小为4的初始水滴,可以理解为水滴大小变成6然后分裂,分裂后移动的水滴互不影响,即使相遇也不会融合在一起,最后一点就是分裂时间是初始水滴满足大小大于4的那一瞬间

比如:

input

1 2 1 1

1 1 4

1 2

output

0 1

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#define Max(a,b) (a>b?a:b)
#define Min(a,b) (a<b?a:b)
#define LL long long
#define MAX 999999999
using namespace std;
int map1[111][111],map2[111][111],n,m,t;
int dd[4][2]={{-1,0},{1,0},{0,-1},{0,1}};
struct node
{
    int x,y,time,f;
}ss,s;
int num[111][2];
bool cmp(int x,int y)
{
    return x>0&&y>0&&x<=n&&y<=m;
}
void bfs()
{
    int i,j,k,l,x,y,time;
    scanf("%d%d",&x,&y);//输入默认水滴分裂位置
    queue<node> q;
    ss.x=x;//初始化
    ss.y=y;
    ss.time=0;
    for(i=0;i<4;i++)//记录分裂后的四个水滴的方向
    {
        ss.f=i;
        q.push(ss);//并且存入队列
    }
    map1[x][y]=0;//默认水滴分裂完毕后清空水滴大小
    while(q.size())
    {
        ss=q.front();
        q.pop();
        if(ss.time>=t)return;//如果超过时间就结束(跳过),用错了,应该是用continue的,呵呵,懒得改了
        ss.x+=dd[ss.f][0];//按照记录的方向移动并改变坐标
        ss.y+=dd[ss.f][1];
        time=ss.time+1;//改变时间
        x=ss.x;
        y=ss.y;
        if(cmp(x,y))//判断是否越界
        {
            if(map1[x][y]>0)//有初始水滴存在
            {
                map1[x][y]++;//融入,大小+1
                if(map1[x][y]>4)//如果大小大于4
                {
                    map2[x][y]=time;//记录初始水滴分裂时间
                    map1[x][y]=0;//大小归零
                    for(i=0;i<4;i++)//分裂成四个方向的小水滴
                    {
                        ss.f=i;
                        ss.time=time;
                        q.push(ss);//把四个方向都放入队列
//记住,放入时的状态为分裂的瞬间,只变方向,不变时间和位置
                    }
                }
            }else
            {//无初始水滴存在
                if(map2[x][y]==time)continue;//如果初始水滴分裂时间和当前相等则跳过,表示当前点也融入后分裂
                ss.time=time;
                q.push(ss);
            }
        }
    }
}
int main (void)
{
    int T,i,j,k,l;
    while(scanf("%d%d%d%d",&n,&m,&T,&t)!=EOF)
    {
        memset(map1,0,sizeof(map1));//记录当前位置的初始水滴大小
        memset(map2,0,sizeof(map2));//记录当前位置水滴分裂时间
        for(i=0;i<T;i++)
        {
            scanf("%d%d%d",&j,&k,&l);
            map1[j][k]=l;//在map1中记录相应位置的初始水滴
            num[i][0]=j;//记录每个初始水滴的位置
            num[i][1]=k;
        }
        bfs();//开始玩游戏
        for(i=0;i<T;i++)
        {
            j=num[i][0];
            k=num[i][1];//cout<<"A"<<j<<" "<<k<<endl;
            if(map1[j][k]==0)
            {
                printf("0 %d\n",map2[j][k]);
            }else
            {
                printf("1 %d\n",map1[j][k]);
            }
        }
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值