2013 ACM-ICPC吉林通化全国邀请赛部分题解

hdu 4597 - Play Game

题型:区间dp,博弈

题意:

有两副牌,每副都有N张牌,每次只能从最上面或者最下面拿牌,每拿到一张牌,就加上这张牌的分数,问Alice先手的话最后她能得到的最大值


思路:

区间dp

dp[l][r][ll][rr] 表示当前(l,r)(ll,rr)这个子牌堆下,此人能够得到的最大分数,那么状态转移方程应该是:

dp[当前区间] = 当前区间的总分值 - 上一状态另一个人能够拿到的分数的最小值

而"上一状态"无非就是四种方式拿牌的情况


代码:

#pragma comment(linker, "/STACK:102400000,102400000")
#include "iostream"
#include "cstring"
#include "algorithm"
#include "cmath"
#include "cstdio"
#include "sstream"
#include "queue"
#include "vector"
#include "string"
#include "stack"
#include "cstdlib"
#include "deque"
#include "fstream"
#include "map"
#define eps 1e-14
using namespace std;
typedef long long LL;
const int MAXN = 1000000+100;
const int INF = 0x6fffffff;
const int inf = 522133279;
const long long LLinf = 1e30;

int sum[2][30];
int dp[30][30][30][30];

int zouqi(int shangl , int shangr , int xial , int xiar)
{
    if(shangl >shangr && xial >xiar)
        return 0;
    if(dp[shangl][shangr][xial][xiar])
        return dp[shangl][shangr][xial][xiar];

    int cur = sum[0][shangr] - sum[0][shangl-1] + sum[1][xiar] - sum[1][xial-1];
    int v=0;

    if(shangl <= shangr)
    {
        v = max(max(v,cur - zouqi(shangl+1,shangr,xial,xiar)),
                 cur - zouqi(shangl , shangr-1 , xial , xiar));
    }

    if(xial <= xiar)
    {
        v = max(max(v,cur - zouqi(shangl,shangr,xial+1,xiar)),
                 cur - zouqi(shangl , shangr , xial , xiar-1));
    }

    return dp[shangl][shangr][xial][xiar] = v;
}

int main()
{
    int t;
    scanf("%d" , &t);

    while(t--)
    {
        memset(dp,0,sizeof(dp));
        memset(sum,0,sizeof(sum));

        int n;
        scanf("%d" , &n);

        for(int i = 0 ; i < 2 ; i++)
            for(int j = 1 ; j <= n; j++)
            {
                scanf("%d" , &sum[i][j]);
                sum[i][j] += sum[i][j-1];
            }

        zouqi(1,n,1,n);

        printf("%d\n" , dp[1][n][1][n]);
    }

    return 0;
}



hdu 4497 - GCD and LCM

题型:质因数分解,组合数学

题意:
给两个32位无符号整数G,L,找出这样三个数:x,y,z 使得它们的最大公约数为G且最小公倍数为L , 求这样的数对个数(不同排列算作不同情况)

思路:
1、
显然如果L不能被G整除的话答案就是0了,公约数公倍数的性质。

2、
令n = L/G,
x,y,z /=n,那么x,y,z互质(注意并非两两互质!!这是本题的重点)且最小公倍数为n.
把n分解成若干个质数相乘(Pi),每个质数拥有自己的一个指数,代表其出现次数。
那么相当于把指数和数量的质数安排进3个数内。

x = P1^a1 * P2^b1 * ... 
y = P1^a2 * P2^b2 * ... 
z = P1^a3 * P2^b3 * ... 

对于某一质数Pi,设总指数为p,将三个数P1^a,P1^b,P1^c放入x,y,z中,由于三个指数互质,所以其中一定会出现一个以上的0(否则x,y,z就不互质了) ,并且要使n为LCM的话,max(a,b,c) = p (否则能找到更小的LCM)

 考虑排列的话,分如下情况:

(1) 一个0,但有两个数相同,3(两个0)+3(两个p) = 6种情况

(2)一个0且另外两数不同,6*(p-1) 种情况

故最后对于每个质数,都有6p种安排法,则最后答案连乘就可以得到了


代码:

#pragma comment(linker, "/STACK:102400000,102400000")
#include "iostream"
#include "cstring"
#include "algorithm"
#include "cmath"
#include "cstdio"
#include "sstream"
#include "queue"
#include "vector"
#include "string"
#include "stack"
#include "cstdlib"
#include "deque"
#include "fstream"
#include "map"
#define eps 1e-14
using namespace std;
typedef long long LL;
const int MAXN = 1000000+100;
const int INF = 0x6fffffff;
const int inf = 522133279;
const long long LLinf = 1e30;

int p[1000];

int main()
{
    int t;
    cin >> t;

    while(t--)
    {
        LL m,n;
        cin >> m >> n;
        memset(p,0,sizeof(p));

        if(n%m)
        {
            cout << 0 << endl;
            continue;
        }

        n/=m;

        int cnt=0;
        LL r = (LL)sqrt(1.0*n)+1;

        for(LL i = 2 ; i < r ; i++)
        {
            if(n%i == 0)
            {
                while(n%i == 0)
                {
                    p[cnt]++;
                    n/=i;
                }
                cnt++;
            }
        }

        if(n != 1)
            p[cnt++]=1;

        LL ans=1;

        for(int i = 0 ; p[i] ; i++)
            ans *= 6*p[i];

        cout << ans <<endl;
    }


    return 0;

}

hdu 4493 - Tutor

题型:输入输出控制
题意:

给十二个小数求平均值,要求不能输出后导0,小数部分最多2位


思路:

这么水的题训练的时候纠结了好久,总之一句话:sprintf和sscanf是个好东西


代码:

#pragma comment(linker, "/STACK:102400000,102400000")
#include "iostream"
#include "cstring"
#include "algorithm"
#include "cmath"
#include "cstdio"
#include "sstream"
#include "queue"
#include "vector"
#include "string"
#include "stack"
#include "cstdlib"
#include "deque"
#include "fstream"
#include "map"
#define eps 1e-14
using namespace std;
typedef long long LL;
const int MAXN = 1000000+100;
const int INF = 0x6fffffff;
const int inf = 522133279;
const long long LLinf = 1e30;

int main()
{
    int t;
    scanf("%d" , &t);

    while(t--)
    {
        double tmp;
        double res=0;

        for(int i = 0 ; i < 12 ; i++)
        {
            scanf("%lf" , &tmp);
            res+=tmp;
        }
        res /= 12;

        char a[20];
        sprintf(a,"%.2lf",res);
        int len = strlen(a);

        if(a[len-1] == '0' && a[len-2] == '0')
            a[len-3] = '\0';
        else if(a[len-1] == '0')
            a[len-1] = '\0';

        printf("$%s\n" , a);
    }

    return 0;
}


hdu 4496 - D-City

题型:并查集
题意:
构造一个图,要求按输入毁边,每去一条边输出这个图的连通块个数

思路:
真的从头开始毁麻烦死你!这题当然是要从最后开始,那时任何一个顶点之间都没有连通,块数为n,之后每建一条边,块数就相应变化
判断两个点是否在同一块中显然用并查集

代码:
//#pragma comment(linker, "/STACK:102400000,102400000")
#include "iostream"
#include "cstring"
#include "string"
#include "cmath"
#include "queue"
#include "cstdio"
#include "algorithm"
#include "cctype"
#include "vector"
#include "map"
#include "stack"
using namespace std;
typedef long long LL;
const int INF = 0x6fffffff;
const int inf = 522133279;
const LL llinf = 1e30;
const double eps = 1e-10;

int n,m;
int f[10010];
int e[100010][2];
stack<int> lis;

int find(int x)
{
    return x == f[x] ? x : f[x] = find(f[x]);
}

int merger(int x,int y)
{
    x = find(x);
    y = find(y);

    if(x != y)
    {
        f[x]=y;
        return 1;
    }

    return 0;
}

int main()
{
    //freopen("input.txt" , "r" , stdin);
    //freopen("out.txt","w",stdout);

    while(~scanf("%d%d" , &n,&m))
    {
        while(!lis.empty())
            lis.pop();

        for(int i = 0 ; i < m ; i++)
            scanf("%d%d" , &e[i][0] , &e[i][1]);

        for(int i= 0 ; i <= n ; i++)
            f[i]=i;

        int cnt=n;
        lis.push(cnt);
        for(int i = m-1 ; i >= 0 ; i--)
        {
            cnt -= merger(e[i][0] , e[i][1]);
            lis.push(cnt);
        }
        lis.pop();

        while(!lis.empty())
        {
            printf("%d\n" , lis.top());
            lis.pop();
        }
    }

    return 0;
}




hdu 4499 - Cannon

题型:搜索

题意:

就是说一个棋盘里最多能放几个中国象棋的炮使得他们不能在一步之内吃到对方

这个棋盘开始有几格是被其他棋子占了的


思路:

棋盘很小,所以爆搜

选择dfs,从上到下,从左往右搜,优先从左往右搜,这样的话只要判断上和左两个方向就可以知道当前位置能不能放了


代码:

//#pragma comment(linker, "/STACK:102400000,102400000")
#include "iostream"
#include "cstring"
#include "string"
#include "cmath"
#include "queue"
#include "cstdio"
#include "algorithm"
#include "cctype"
#include "vector"
#include "map"
#include "stack"
using namespace std;
typedef long long LL;
const int INF = 0x6fffffff;
const int inf = 522133279;
const LL llinf = 1e30;
const double eps = 1e-10;

int n,m,q;
int g[10][10];
int maxc;

int ok(int x , int y)
{
    int yes=1;

    for(int i = x-1 ; i >= 0 ; i--)         //上方
        if(g[i][y])
        {
            int cnt=0;
            for(int j = i-1 ; j >= 0 ; j--)
            {
                if(g[j][y] == 1)
                    cnt++;
                if(g[j][y] == 2 && cnt == 0)
                {
                    yes=0;
                    break;
                }
            }
            break;
        }

    if(!yes)
        return 0;

    for(int i = y-1 ; i >= 0 ; i--)         //左方
        if(g[x][i])
        {
            int cnt=0;

            for(int j = i-1 ; j >= 0 ; j--)
            {
                if(g[x][j] == 1)
                    cnt++;
                if(g[x][j] == 2 && cnt == 0)
                {
                    yes=0;
                    break;
                }
            }
            break;
        }

    return yes;
}

void dfs(int x , int y , int cnt)
{
    if(x >= n)
    {
        maxc = max(maxc , cnt);
        return;
    }

    if(y >= m)
    {
        dfs(x+1,0,cnt);
        return;
    }

    if(g[x][y])
    {
        dfs(x,y+1,cnt);
        return;
    }

    if(ok(x,y))
    {
        g[x][y]=2;
        dfs(x,y+1,cnt+1);
        g[x][y]=0;
    }

    dfs(x,y+1,cnt);     //不管能不能放,最后还是要走下去的,刚开始把这句放在else里就错了
}

int main()
{
    //freopen("input.txt" , "r" , stdin);
    //freopen("out.txt","w",stdout);

    while(~scanf("%d%d%d" , &n,&m,&q))
    {
        maxc = -1;
        memset(g,0,sizeof(g));

        for(int i = 0 ; i < q ; i++)
        {
            int a,b;
            scanf("%d%d" , &a,&b);
            g[a][b]=1;
        }

        dfs(0,0,0);

        printf("%d\n",maxc);

    }
    return 0;
}



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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值