JNU周练1013

CodeForces 337A

 给出n、m(n<=m)和m个数字,在m个数字中选n个数字,令这n个数字中最大数与最小数值差最小。

数据量小,直接暴力

ExpandedBlockStart.gif
#include <iostream>
#include <algorithm>
using  namespace std;

int main()
{
     int a[ 60], n, m;
    cin >> n >> m;
     for( int i =  0; i < m; i++)
        cin >> a[i];
    sort(a, a+m);
     int min =  1005;
     for( int i= 0; i<=m-n; i++)
    {
         if(a[i+n- 1] - a[i] < min)
            min = a[i+n- 1] - a[i];
    }
    cout << min << endl;
     return  0;
}
View Code 

 

CodeForces 337B

给出屏幕与电影的宽高比,电影按比例缩放,尽量占满屏幕,求屏幕没被占的面积的比例。输出要求:分子分母互质。

 

找规律,无论是电影适应屏幕还是屏幕适应电影,得出的比例都是一样的。同时注意两比例相同时输出0/1。

ExpandedBlockStart.gif
#include <iostream>
using  namespace std;

int gcd( int x,  int y)
{
     int r;
     while(y!= 0)
    {
        r = x%y;
        x = y;
        y = r;
    }
     return x;
}

int main()
{
     int a, b, c, d, temp;
    cin >> a >> b >> c >> d;
    a = a * d;
    c = c * b;
     if(a < c)  
    {
        temp = a;
        a = c;
        c = temp;
    }
    c = a - c;
    temp = gcd(a,c);
    a /= temp;
    c /= temp;
    cout << c <<  " / " << a << endl;
     return  0;
}
View Code 


CodeForces 337C

/*
    题意:
    一人去参加比赛。每答对一题得一分。连续答对k题时先加一分然后总分数乘2然后连续答对题数归0。
    然后给你题目总数n和他答对题目的个数m问你他能得的最低分是多少。

    要获得最低分,要尽量避免连续答对k题,或者让连续答对k题的情况尽量出现在分数少的时候
    方法是:设答对为1,答错为0,以k-1个"1"和1个"0"为一组从后往前排列,
    最后得出两部分,一部分是x组k-1个"1"和1个"0",另一部分为连续y个1
    则n = k * x + y;  m = (k-1) * x + y;  ---> 得 x = n - m

    第一部分好求:(k-1) * x
    第二部分需要找规律:
    另z = y / k,则有z*k次答对,取z = 1, 2, 3,...总分数如下
    那么分数变化为:2*k,(2*k+k)*2,((2*k+k)*2+k)*2......。
    抽象出来这就是一个数列递推公式为:a[z]=2*(a[z-1]+k)。
    展开移项后得:a[z]+2*k=2*(a[z-1]+2*k)。设c[z]=a[z]+2*k。那么c[z]/c[z-1]=2。等比数列。
    而c[0]=a[0]+2*k=2k。那么c[z]=2*k*2^z。所以a[z]=k*2^(z+1)-2*k。

    数据大,要用到快速幂


*/

 注意中间数大,要开long long

ExpandedBlockStart.gif
#include <iostream>
using  namespace std;

const  long  long MOD =  1000000009;

// 计算a^bmodn     
long  long modexp_recursion( long  long a, long  long b, long  long n)     
{    
     long  long t =  1;

     if (b ==  0)
         return  1;

     if (b ==  1)
          return a%n;

    t = modexp_recursion(a, b>> 1, n);

    t = t*t % n;

     if (b& 0x1)
    {    
        t = t*a % n;
    }

     return t;
 } 

int main()
{
     long  long n, m, k;
    cin >> n >> m >> k;
     long  long a = m / (k- 1);
     if(a <= n - m)
        cout << m << endl;
     else
    {
         long  long ret =  0;
        a = n - (n-m)*k;
         long  long b = a / k;
         if(b> 0)
        {
             // cout << "b:" << b << endl;
            
// cout << modexp_recursion(2, b, MOD) << endl;
            ret =  2 * k * modexp_recursion( 2, b, MOD) -  2*k;
            ret = ret % MOD;
             // cout << "ret:" << ret << endl;
        }
        ret = (ret + (n-m) * (k- 1)) % MOD;
        ret = (ret + (a-a/k*k)) % MOD;
        cout << ret << endl;
         // cout << "aaa:" << modexp_recursion(2,100,MOD) << endl;
    }
     return  0;
}
View Code 

 

CodeForces 337D

题意:给一棵n个结点的树,任意两个节点的距离是指连接两点的最短的边数

在树上的某个结点有一个“恶魔之书”,这本书会让距离它d以内的节点都受到影响

已知有m个节点收到了影响,问最多有几个结点可能放着“恶魔之书”?

 

据说是树形dp,看题解知道大概思路后,自己写代码,挑了无数bug过了。。

规定点1为根节点 

dp[i][0] --- 代表节点 i 子树中距 i 最远的受影响点和 i 的距离。

dp[i][1] --- 上述的次远距离

dp[i][2] --- 代表节点 i 子树以外的部分 距 i 最远的受影响点和 i 的距离。

若某点dp[i][0] 和 dp[i][2]均小于或等于影响范围d,表示该点满足条件。

 dp[i][0] 和 dp[i][1]好求,dp[i][2] 则要综合(1) i 的祖先节点和(2)其父节点的其他子树的情况。

 

状态转移方程: 

u(父)--- v(子)

fa(父) --- u, i (子)

dp[u][0] = max(dp[v][0] + 1)

dp[u][2] =  max(dp[fa][2], {dp[fa][0] or dp[fa][1]} + 1)   

 

ExpandedBlockStart.gif
#include <iostream>
#include <cstring>
#include <algorithm>
using  namespace std;
#define N 100010
#define M 200010

int head[N], vid[N], vis[N], eNum, n, m, d;
struct Edge
{
     int e;
     int next;
}edge[M];

int dp[N][ 3];

void AddEdge( int u,  int v)
{
    edge[eNum].e = v;
    edge[eNum].next = head[u];
    head[u] = eNum++;
}

void init()
{
     int temp, u, v;
    eNum =  0;
    memset(head, - 1sizeof(head));
    memset(vis,  0sizeof(vis));
    memset(dp, - 1sizeof(dp));
    cin >> n >> m >> d;
     for( int i= 1; i<=m; i++)
    {
        cin >> temp;
        vis[temp] =  1;
    }
     for( int i= 1; i<n; i++)
    {
        cin >> u >> v;
        AddEdge(u, v);
        AddEdge(v, u);
    }
}

void dfs1( int u,  int fa)
{
     if(vis[u]== 1) dp[u][ 0] =  0;
     for( int i = head[u]; i != - 1; i = edge[i].next)
    {
         int v = edge[i].e;
         if(v == fa)  continue;
        dfs1(v, u);
         if(dp[v][ 0] +  1 > dp[u][ 0])
        {
             if(dp[v][ 0]!=- 1)
            {
                dp[u][ 1] = dp[u][ 0];
                dp[u][ 0] = dp[v][ 0] +  1;
            }
        }
         else  if(dp[v][ 0] +  1 > dp[u][ 1] && dp[v][ 0] != - 1)
        {
            dp[u][ 1] = dp[v][ 0] +  1;
        }

    }
}

void dfs2( int u,  int fa)
{
     if(vis[u]== 1 && dp[u][ 2]==- 1) dp[u][ 2] =  0;
     for( int i=head[u]; i != - 1; i = edge[i].next)
    {
         int v = edge[i].e;
         if(v == fa)  continue;
         if(dp[u][ 2]!=- 1) dp[v][ 2] = dp[u][ 2] +  1;
         if(dp[v][ 0]+ 1 == dp[u][ 0])
        {
             if(dp[u][ 1] != - 1)
                dp[v][ 2] = max(dp[v][ 2], dp[u][ 1]+ 1);
        }
         else  if(dp[u][ 0]!=- 1)
            dp[v][ 2] = max(dp[v][ 2], dp[u][ 0]+ 1);
        dfs2(v, u);
    }
}

int main()
{
    init();
    dfs1( 10);
    dfs2( 10);
     int ans =  0;
     for( int i= 1; i<=n; i++)
         if(dp[i][ 0]<=d && dp[i][ 2]<=d)
            ans++;
     // for(int i=1; i<=n; i++)
    
// {
    
//     cout << dp[i][0] << "   " << dp[i][1] << "   " << dp[i][2] << endl;
    
// }
    cout << ans << endl;
     return  0;
}
/*
9 4 3
1 2 8 9
1 5
5 4
4 3
3 2
5 6
6 7
7 8
8 9

*/
View Code 

 

 CodeForces 337E

 题意,给你一颗n个数,构建一颗树,包含所有数,满足根结点等于儿子结点的乘积,并且所有叶子结点都是素数。

 

 

关键点:对每个a[i]分解质因子,得出因子个数。在此基础上建树,由于n<=8,所以直接dfs枚举所有情况,得出最小的结点数。 

ExpandedBlockStart.gif
#include <iostream>
#include <algorithm>
#include <cstring>
using  namespace std;
struct Num
{
     long  long b;
     int c;
}num[ 10];
int n;  // 存输入
int a[ 80000], index;
long  long leftt[ 10]; // 标记剩余多少
int vis[ 10], sum, ans_min;

int prime( int x)
{
     int flag =  0;
     for( int i= 2; i*i<=x; i++)
    {
         if(x%i== 0)
        {
            flag =  1;
             break;
        }
    }
     if(flag)
         return  0;
     return  1;
}

// 筛选法求1000000内素数
int prime1( int x)
{
     int flag =  0;
     for( int i= 0; a[i]*a[i]<=x; i++)
    {
         if(x%a[i]== 0)
        {
            flag =  1;
             break;
        }
    }
     if(flag)
         return  0;
     return  1;
}

bool cmp(Num a, Num b)
{
     return a.c < b.c;
}

void dfs( int t)
{
     // cout << "t: " << t << endl;
     if(t == n)
    {
         int ret = sum, retn = n;
         for( int i= 1; i<=n; i++)
        {
             if(vis[i]!= 0)
            {
                ret -= num[i].c;
                retn --;
            }
        }
         if(retn> 1) ret++;
         if(ret < ans_min) ans_min = ret;
         // ------test
        
// if(ret==8)
        
// {
        
//     for(int i=1; i<=n; i++)
        
//     {
        
//         cout << vis[i]<<endl;
        
//     }
        
// }
         return ;
    }

     for( int i=t+ 1; i<=n; i++)
    {
         long  long temp = leftt[i];
         if(leftt[i] >= num[t].b && leftt[i]%num[t].b== 0)
        {
            leftt[i] /= num[t].b;
            vis[t] = i;
        }
        dfs(t+ 1);
         if(temp!=leftt[i])
        {
            leftt[i] *= num[t].b;
            vis[t] =  0;
        }
    }
}

int main()
{
     long  long flag;
    index =  0;
     for( int i= 2; i<= 1000; i++)
    {
         if(prime(i))
            a[index++] = i;
    }
     for( int i= 1001; i<= 1000000; i++)
    {
         if(prime1(i))
            a[index++] = i;
    }

    cin >> n;
     for( int i= 1; i<=n; i++)
        cin >> num[i].b;
     for( int i= 1; i<=n; i++)
    {
        num[i].c =  0; flag = num[i].b;
         for( int j= 0; j<index && flag> 1; )
        {
             if(flag%a[j]== 0)
            {
                flag /= a[j];
                num[i].c ++;
            }
             else
            {
                j++;
            }
        }
         // 原数<=10^12,除遍10^6内的素数后,若该数仍大于0,则一定有且仅一个10^6+1~10^12的质因子
         if(flag> 1) num[i].c++;
    }
    sort(num+ 1, num+n+ 1, cmp);

    sum =  0, ans_min =  1000000000;
    memset(vis, 0, sizeof(vis));
     for( int i= 1; i<=n; i++)
    {
        leftt[i] = num[i].b;
        sum += num[i].c;
         if(num[i].c!= 1) sum++;
    }
     // cout << "sum: " << sum << endl;
    dfs( 1);
    cout << ans_min << endl;

     // for(int i=1; i<=n; i++)
    
//     cout << num[i].b << ": " << num[i].c << endl;
    
// cout << index << endl;
    
// cout << a[index-1] << endl;
     return  0;
}
View Code 

 

 

转载于:https://www.cnblogs.com/byluoluo/p/3369584.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值