2016 杭州区域赛补题

A - ArcSoft's Office Rearrangement

 HDU - 5933 

题意:现在有n个办公区,每个办公区内有a[i]个办公人员,现在要将这些人均匀的分布在m个办公区内,每个办公区人员必须等价。现在有两个操作,1操作,可以将相邻的两个区域的人员结合在一起,2操作可以将一个办公区的人员,随意划分成两部分,两部分不一定平均,问最少需要多少次操作。

做法:贪心,从前向后枚举,不可以的情况就是不能平均的,可以预处理出来平均值,对于可以的情况,如果比平均值小,就直接加在后一个办公区内,要是与平均值一样,就可以直接跳过,要是大于平均值,就先划分成平均值的若干份,最后不足一个平均值的加在后一个办公区内。

代码如下:

#include<stdio.h>
#include<iostream>

using namespace std;

long long t;
long long n , m;
long long a[100005];
long long sum;
long long num;

int main()
{
    scanf("%lld" , &t);
    for(long long cas = 1; cas<=t; cas++)
    {
        scanf("%lld%lld" , &n , &m);
        sum = 0;
        num = 0;
        for(long long i=1; i<=n; i++)
        {
            scanf("%lld" , &a[i]);
            sum += a[i];
        }
        if(sum % m != 0)
        {
            printf("Case #%lld: -1
" , cas);
        }
        else
        {
            long long tmp = sum/m;
//            printf("%lld..
" , tmp);
            for(long long i=1; i<=n; i++)
            {
//                printf("%lld...%lld..
" , i , a[i]);
                if(a[i] > tmp)
                {
                    num += (a[i]/tmp)-1;
                    if(a[i]%tmp != 0)
                    {
                        num ++;   //先剥离下来
                        num++;    //再安装到下一个块上去
                        a[i+1] += a[i]%tmp;
                    }
                }
                else if(a[i] == tmp)
                {
                    continue;
                }
                else if(a[i]<tmp && a[i]!=0)
                {
                    num++;
                    a[i+1] += a[i];
                }
            }
            printf("Case #%lld: %lld
" , cas , num);
        }
    }


    return 0;
}
View Code

F - Four Operations

 HDU - 5938

题意:给你一个字符串,5<=len<=20,字符串由1~9的数字组成,问你向里面按顺序添加+ - * / 构成的算术运算式子结果最大是多少

做法:我们知道肯定让x+y尽可能的大,a*b/c的结果尽可能小,于是a*b的结果尽可能小,c尽可能大。

于是我们枚举“/”的位置,后面的作为c,a,b都只占一位 前面的作为x+y,一直x+y的长度之后,例如12121,那么和要尽可能大,要么就是1+2121,要么就是1212+1,因为这样可以保证尽可能长的长度,为什么要枚举除法的位置,而不是直接取最后一位,给前面省长度呢,因为可能a*b很大,c很小,导致我们减去的东西很大,比一位所带来的影响还要大,例如224591,所以需要枚举"/"的位置

代码如下:

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

using namespace std;

int t;
char s[25];
int len;
long long sum[25];
long long maxx;

long long num(int l , int r)
{
    long long res = 0;
    for(int i=l; i<=r; i++)
    {
        res = res*10+(s[i]-'0');
    }
    return res;
}

int main()
{
    scanf("%d" , &t);
    for(int cas=1; cas<=t; cas++)
    {
        scanf("%s" , s);
        len = strlen(s);
        maxx = -100000000000000000;
//        printf("%lld..
" , maxx);
        for(int i=0; i<len; i++)
        {
//            sum[i] =0;
//            for(int j=0; j<i; j++)
//            {
//                sum[i] = max(sum[i] , num(0,j)+num(j+1,i));
//            }
          sum[i] = max(num(1,i)+(s[0]-'0') , num(0,i-1)+(s[i]-'0'));
//          printf("%d..%lld..
" , i , sum[i]);
        }
        for(int i=4; i<len; i++)
        {
            maxx = max(maxx , sum[i-3]-((s[i-2]-'0')*(s[i-1]-'0')/num(i,len-1)));
//            printf("%d...%lld...
" , i , maxx);
        }
        printf("Case #%d: %lld
" , cas , maxx);
    }

    return 0;
}


/*
100
99999999999999999999


1224591


224591
*/
View Code

C - Car

 HDU - 5935

题意:现在一个人驾车行驶,保证全程不减速,现在给出了n个测速点,保证测速点一定是在整秒的时候测量的,问这个人通过这n个测速点最快的时间

做法:正向的,全程不减速不好写,对于每一段,你无法确定速度与时间,如果这一段比上一段长,你就直接设置成一秒,那万一下一段很短,岂不是前面的设置都错了;

于是我们反向想,倒过来就是全程不加速,那么最后一段一定是一秒,速度最大,这样可以保证全程时间尽可能的短,于是最后一段的时间与速度都是知道的,那么前一段的速度要小于这一段,时间要尽可能的短,于是整除向上取整即可,记得更新速度,即可解决

坑点:不能直接计算速度v  精度卡的非常的死  必须保存距离与时间

代码如下:

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

using namespace std;

int t;
int n;
long long b[100005];
long long a[100005];
//long long num1 , num2;

void debug()
{
    for(int i=1; i<=n; i++)
    {
        printf("%d..%lld...
" ,i , a[i]);
    }
}

int main()
{
    scanf("%d" , &t);
    for(int cas=1; cas<=t; cas++)
    {
        scanf("%d" , &n);
//        num1 = 0;
        b[0] = 0;
        for(int i=1; i<=n; i++)
        {
            scanf("%lld" , &b[i]);
//            a[i] = num2-num1;
//            num1 = num2;
        }
        sort(b+1 , b+1+n);
        for(int i=1; i<=n; i++)
        {
            a[i] = b[i]-b[i-1];
        }
//        debug();
        long long ans = 1;
//        double v = a[n]*1.00;
        long long tmp;
        tmp = 1;
        for(int i=n-1; i>=1; i--)
        {
            tmp = ceil((1.00*a[i]*tmp)/a[i+1]);
//            printf("%d..%lld..
" , i , tmp);
            ans += tmp;
        }
        printf("Case #%d: %lld
" , cas , ans);
    }


    return 0;
}
View Code

B - Bomb

 HDU - 5934

题意:现在有一对炸弹,要全部都引爆,每个炸弹有自己的圆心和半径,引爆之后在范围内的炸弹都会被引爆,问引爆所有炸弹所需要的最小的花费。n<=1e3

做法:刚开始的做法是,首先,我们可以根据引爆范围n^2的建图,有向图,入度为零的是一定要点的,它所能到达的点全部都删去, 剩下的就是在连通块中找最小花费的点了。剩下的这部分我是直接联通快中招最小点的。 这样是一定不对的,例如,1-2 2-3 3-1 2-4 4的值最小,但是引爆4并不能引爆1,2,3. 于是,只有在同一个强连通分量里面才可以求最小值,剩下的就是入度为零的全部引爆了 于是,先缩点,形成DAG之后求 Tarjan的时间复杂度O(n+m) 缩点时间复杂度O(n^2) 求解O(n)

代码如下:

/*
刚开始的做法是:首先,我们可以根据引爆范围n^2的建图,有向图,入度为零的是一定要点的,它所能到达的点全部都删去,
剩下的就是在连通块中找最小花费的点了。剩下的这部分我是直接联通快中招最小点的。
这样是一定不对的,例如,1-2 2-3 3-1 2-4 4的值最小,但是引爆4并不能引爆1,2,3.
于是,只有在同一个强连通分量里面才可以求最小值,剩下的就是入度为零的全部引爆了
于是,先缩点,形成DAG之后求
Tarjan的时间复杂度O(n+m)
缩点时间复杂度O(n^2)
求解O(n)
*/
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<math.h>

using namespace std;

const int maxn = 1003;

int t;
int n;

struct CIRCLE
{
    double x,y;
    double r;
    int c;
}circle[maxn];

double dis(int i , int j)
{
    return sqrt((circle[i].x-circle[j].x)*(circle[i].x-circle[j].x)+(circle[i].y-circle[j].y)*(circle[i].y-circle[j].y));
}

void input()
{
    for(int i=1; i<=n; i++)
    {
        scanf("%lf%lf%lf%d" , &circle[i].x , &circle[i].y , &circle[i].r , &circle[i].c);
    }
}

struct EDGE
{
    int to , next;
}edge[maxn*maxn];
int head[maxn] , tot;
//int head2[maxn] , tot2;
int ans;
int du[maxn];

int low[maxn] , dfn[maxn] , Stack[maxn] , num[maxn];///num记录缩点之后每个点的大小
int belong[maxn];
int index , scc , top;
bool InStack[maxn];

void init()
{
    memset(head , -1 , sizeof(head));
    tot = 0;
//    memset(head2 , -1 , sizeof(head2));
//    tot2 = 0;
    ans = 0;
    for(int i=1; i<=n; i++)
    {
        num[i] = 10004;
    }
    memset(du , 0 , sizeof(du));
}

void add(int u , int v)
{
    edge[tot].to = v;
    edge[tot].next = head[u];
    head[u] = tot++;
}

//void add2(int u , int v)
//{
//    edge2[tot2].to = v;
//    edge2[tot2].next = head2[u];
//    head2[u] = tot2++;
//}

void build()
{
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=n; j++)
        {
            if(j == i) continue;
            if(dis(i , j) <= circle[i].r)
            {
                add(i , j);
            }
        }
    }
}

void Tarjan(int u)
{
    int v;
    low[u] = dfn[u] = ++index;
    Stack[top++] = u;
    InStack[u] = true;
    for(int i=head[u]; i!=-1; i=edge[i].next)
    {
        v = edge[i].to;
        if( !dfn[v] )
        {
            Tarjan(v);
            if( low[u] > low[v] )
            {
                low[u] = low[v];
            }
        }
        else if(InStack[v] && low[u] > dfn[v])
        {
            low[u] = dfn[v];
        }
    }
    if(low[u] == dfn[u])
    {
        scc++;
        do
        {
            v = Stack[--top];
            InStack[v] = false;
            belong[v] = scc;
            num[scc] = min(num[scc] , circle[v].c);
        }while(v!=u);
    }
}

void solve()
{
    memset(dfn , 0 , sizeof(dfn));
    memset(InStack , false , sizeof(InStack));
    index = scc = top = 0;
    for(int i=1; i<=n; i++)
    {
        if( !dfn[i] )
        {
            Tarjan(i);
        }
    }
    for(int i=1; i<=n; i++)
    {
        for(int j=head[i]; j!=-1; j=edge[j].next)
        {
            int tmp = edge[j].to;
            if(belong[i] != belong[tmp])
            {
//                add2(belong[i] , belong[tmp]);
                  du[belong[tmp]]++;
            }
        }
    }

}

int main()
{
    scanf("%d" , &t);
    for(int cas=1; cas<=t; cas++)
    {
        scanf("%d" , &n);
        init();
        input();
        build();
        solve();
        for(int i=1; i<=scc; i++)
        {
            if(du[i] == 0)
            {
                ans += num[i];
            }
        }
        printf("Case #%d: %d
" , cas , ans);
    }

    return 0;
}
View Code

 题意:现在定义一个操作,对于正整数,强调一下,是正整数,数字y的每一位的K次方-y=x,现在给定了x与k,求有多少种满足条件的y

做法:x = F(y,k)-y

         因为  y > 0

         所以 x > F(y,k)

         因为 题目给定 x > 0

         所以  F(y,k) > y

         所以x > F(y,k) > y

         所以y的长度不超过x的长度10位,将y分成两部分,低位部分a和高位部分b

        设 F(Y,K) = F(a,k) +F(b,k);

        x = F(a,k) +F(b,k)-a- b*1e5;

        x-F(a,k)+a = F(b,k)-b*1e5

         y 的个数就是使上式成立的数字的个数

       于是枚举等式前边的数字,时间复杂度O(100000),预处理出来等式后边的,排序之后二分查找使之成立的个数即可

感谢:https://blog.csdn.net/luricheng/article/details/70176894提供思路与坑点

代码如下:

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

using namespace std;

const long long maxn = 100050;

long long F[maxn];
long long t;
long long x , k;
long long ans[maxn];

long long  poww(long long n ,long long m)
{
    long long  res = 1;
    for(long long i=1; i<=m; i++)
    {
        res *= n;
    }
    return res;
}

void init()
{
    memset(F , 0 , sizeof(F));
    for(long long i=0; i<100000; i++)
    {
        long long j = i;
        while( j )
        {
            F[i] += poww(j%10 , k);
            j /= 10;
        }
        ans[i] = F[i]-(i*100000);
    }
    sort(ans , ans+100000);
}

void debug()
{
    for(long long i=0; i<=100; i++)
    {
        printf("%lld...%lld...%lld
" , i , F[i] , ans[i]);
    }
}

long long sea(long long  xx)
{
   long long  L , R;
   L = lower_bound(ans , ans+100000 , xx)-ans;
   if(ans[L] != xx)
    return 0;
   R = upper_bound(ans , ans+100000 , xx)-ans;
   R--;
   if(ans[R] != xx)
    return 0;
   return R-L+1;
}

long long solve()
{
    long long  res;
    res = 0;
    for(long long i=0; i<100000; i++)
    {
        long long  tmp = sea(x-F[i]+i);
        res += tmp;
    }
    return res;
}

int main()
{
    scanf("%lld" ,&t);
    for(long long cas=1; cas<=t; cas++)
    {
        scanf("%lld%lld" , &x , &k);
        init();
//        debug();
        long long tmp = solve();
        if(x == 0)
            tmp--;
        printf("Case #%lld: %lld
" , cas , tmp);
    }


    return 0;
}
View Code

题意:现在你有一些数字卡片,每个数字卡片上写有1~9中的一个,给你的是每一个数字有多少张卡片,问你能够组成多少种不一样的等式。

A+B=C 与 B+A=C 定义为不一样的

思路:感谢https://www.cnblogs.com/jihe/p/6024135.html不然这个bug我能de一年

直接暴力哪几个被选了就好,但是不能这样暴力,就是你现在选的是第x个,下一个选第几个,因为这样只让我们的暴力数量变多,我们其实想要的就是这个有没有被选就好了

那么就从第一个开始,看他作为第i个的时候,下一个作为第i+1个,与此同时,下一层就是我不作为第i层,i+1作为第i层,这样可以覆盖所有的情况

代码如下:

/*
错因: 首先不剪枝会WA
其次,问的是会出现多少种不一样的,而不是多少个
多少种的话,就可以整一个vis数组,最大容量是2,也就是2+3=5与3+2=5不一样
但是容量是2的时候和直接保存两个有区别吗?
试一下

*/

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

using namespace std;

struct NODE
{
    int a , b , c;
} node[40];
int t;
int maxx;
int a[15];
int rem;
int flag;

void pre()
{
    int tot = 0;
    for(int i=1; i<9; i++)
    {
        for(int j=1; i+j<=9; j++)
        {
                node[tot].a = i;
                node[tot].b = j;
                node[tot].c = i+j;
//                printf("%d..%d..%d..%d..
" , tot , i , j , i+j);
                tot++;
        }
    }
}

void init()
{
    maxx = 0;
    flag = 1;
    rem = 0;
}

void input()
{
    for(int i=1; i<=9; i++)
    {
        scanf("%d" , &a[i]);
        a[i] = min(a[i] , 17-i);
        rem += a[i];
        if(a[i] < 17-i)
            flag = 0;
    }
}

bool ok(int xx)
{
    if(a[node[xx].a]>=0 && a[node[xx].b]>=0 && a[node[xx].c]>=0)
        return true;
    return false;
}

void dfs(int x , int dep , int sheng)
{
    if(36-x+dep<=maxx)   ///后面的全都可以也不行的话 就直接跳出
        return ;
    if(dep >= 36)
        return ;
    a[node[x].a] -= 1;
    a[node[x].b] -= 1;
    a[node[x].c] -= 1;
    if(ok(x))
    {
        maxx = max(maxx , dep+1);
        dfs(x+1 , dep+1 , sheng-3);
    }
    a[node[x].a] += 1;
    a[node[x].b] += 1;
    a[node[x].c] += 1;
    dfs(x+1 , dep , sheng);
}

void solve()
{
    dfs(0 , 0 , rem);
}

int main()
{
    pre();
    scanf("%d" , &t);
    for(int cas=1; cas<=t; cas++)
    {
        init();
        input();
//        printf("%d...
" , rem);
        if(flag)
        {
            printf("Case #%d: 36
" , cas);
            continue;
        }
        solve();
        printf("Case #%d: %d
" , cas , maxx);
    }

    return 0;
}
View Code
原文地址:https://www.cnblogs.com/Flower-Z/p/9703607.html