2014多校训练九(HDU 4960 HDU 4961 HDU 4965 HDU 4968 HDU 4969 HDU 4970)

HDU 4960 Another OCD Patient

题意:给你一串数字  相邻x个数字合并成一个数字(相加)有一定代价  问  最少花费多少使得串变成回文串

思路:

读完题感觉像dp  数据范围也像  就开始想怎么表示状态  最简单的应该想到dp[i][j]表示i到j区间变成回文串的最小花费  状态想好了想做法  考虑将串分成AAAABBBBBBBCCC三段  即所有A合成一个数字  C也是  而且A和C相等  那么B串就变成了子问题  但是A和C是不是都要枚举呢?  这个串所有元素都是正的  那么也就是对于一种A只能对应一种C  所以这个枚举复杂度是O(n)的  而且每次做子问题都会至少去掉首尾2个元素  那么复杂度也就是n^2级别

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 5010
#define inf 2000000000
typedef __int64 LL;

int n,amt;
int cost[N],dp[N][N];
LL sum[N];

int dfs(int l,int r)
{
    if(l>=r) return 0;
    if(dp[l][r]!=inf) return dp[l][r];
    int i,j;
    LL suml,sumr;
    dp[l][r]=cost[r-l+1];
    for(i=l,j=r;i<j;)
    {
        suml=sum[i]-sum[l-1];
        sumr=sum[r]-sum[j-1];
        if(suml==sumr)
        {
            dp[l][r]=min(dp[l][r],dfs(i+1,j-1)+cost[i-l+1]+cost[r-j+1]);
            i++;
            j--;
        }
        else if(suml>sumr) j--;
        else i++;
    }
}

int main()
{
    int i,j;
    while(~scanf("%d",&n))
    {
        if(!n) break;
        for(i=1;i<=n;i++)
        {
            scanf("%I64d",&sum[i]);
            sum[i]+=sum[i-1];
        }
        for(i=1;i<=n;i++) scanf("%d",&cost[i]);
        for(i=1;i<=n;i++)
        {
            for(j=1;j<=n;j++) dp[i][j]=inf;
        }
        dfs(1,n);
        printf("%d\n",dp[1][n]);
    }
    return 0;
}

HDU 4961 Boring Sum

题意:略

思路:

队友做的  他一眼就看出来了…- -b  膜拜猪神!  做法就是正反扫两边  对于每个数  维护它的所有约数  这样b和c数组相当于已知了  最后扫一遍求和

代码:


#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
typedef __int64 ll;
const ll mod = 1000000009;
const double eps = 1e-9;
const double pi = acos(-1.0);
const int M=100005;
int n,m;
vector<int > F[M];

void init()
{
    int i,j;
    for(i=1;i<M;i++)
        for(j=i;j<M;j+=i)
            F[j].push_back(i);
}
int dpa[M],dpb[M];
int ansa[M],ansb[M];
int a[M];
int main()
{
    init();
    int i,j;
    int T,ca=0;
    while(~scanf("%d",&n))
    {
        memset(dpa,0,sizeof(dpa));
        memset(dpb,0,sizeof(dpb));
        if(!n)
            break;
        for(i=1;i<=n;i++)
            scanf("%d",&a[i]);
        for(i=1;i<=n;i++)
        {
            if(!dpa[a[i]])
                ansa[i]=i;
            else
                ansa[i]=dpa[a[i]];
            for(j=0;j<F[a[i]].size();j++)
            {
                int now=F[a[i]][j];
                dpa[now]=i;
            }
        }
        for(i=n;i>=1;i--)
        {
            if(!dpb[a[i]])
                ansb[i]=i;
            else
                ansb[i]=dpb[a[i]];
            for(j=0;j<F[a[i]].size();j++)
            {
                int now=F[a[i]][j];
                dpb[now]=i;
            }
        }
        ll ans=0;
        for(i=1;i<=n;i++)
        {
            int x,y;
            x=a[ansa[i]];y=a[ansb[i]];
            ans=ans+(ll)x*y;
        }
        printf("%I64d\n",ans);
    }
    return 0;
}
HDU 4965 Fast Matrix Calculation

题意:给你矩阵A和B  分别是1000*6和6*1000的  求 C=AB  X=C^(n^2)  sum(C[i][j]%6)

思路:

利用矩阵相乘的结合律  ABABAB...ABAB = A (BA)(BA)... (BA) B 所有的BA可以处理成6*6矩阵快速幂搞  线性代数内容

代码:


#include <iostream>
#include <string.h>
#include <stdio.h>
using namespace std;

int N;
int MOD=6;

typedef struct
{
    int m[100][100];
}Matrix;
void clear(Matrix &a)
{
    memset(a.m,0,sizeof(a.m));
}

Matrix E;

void danweiE()//单位矩阵!!!!
{
    int i,j;
    for(i=0;i<100;i++)
    for(j=0;j<100;j++)
        if(i==j)E.m[i][j]=1;
        else E.m[i][j]=0;
}

Matrix Add(Matrix a,Matrix b)  //矩阵加法(%MOD)  
{  
    int i,j;
    Matrix c;  
    for (i=0; i<N; i++)  
      for (j=0; j<N; j++)  
      {  
          c.m[i][j]=a.m[i][j]+b.m[i][j];  
          c.m[i][j]%=MOD;  
      }  
    return c;  
}

Matrix multi(Matrix a,Matrix b)//矩阵相乘!!!
{
    Matrix c;
    int i,j,k;
    clear(c);
    for(i=0;i<N;i++)
        for(j=0;j<N;j++)
            if(a.m[i][j])
            for(k=0;k<N;k++)
        if(b.m[j][k])
                c.m[i][k]=(c.m[i][k]+a.m[i][j]*b.m[j][k])%MOD;
    return c;
}

Matrix matrix_mod(Matrix a,int k)//矩阵快速幂!!!
{
    danweiE();
    Matrix ans=E,p=a;  
    while(k)
    {
        if(k&1)
        {
            ans=multi(ans,p);
            k--;
        }
        k>>=1;
        p=multi(p,p);
    }
    return ans;
}
int x[1010][1010],y[1010][1010],tmp[1010][1010];
int n,k;
Matrix a,ans;
void debug()
{
    int i,j;
    for(i=0;i<n;i++)
        for(j=0;j<k;j++)
            printf("%d%s",x[i][j],j==k-1?"\n":" ");
    for(i=0;i<k;i++)
        for(j=0;j<n;j++)
            printf("%d%s",y[i][j],j==n-1?"\n":" ");    
    for(i=0;i<k;i++)
        for(j=0;j<k;j++)
            printf("%d%s",a.m[i][j],j==k-1?"\n":" ");
    for(i=0;i<k;i++)
        for(j=0;j<k;j++)
            printf("%d%s",ans.m[i][j],j==k-1?"\n":" ");        
} 
int main()
{   
    int i,j,ii;
    while(scanf("%d%d",&n,&k))
    {
        if(!n&&!k)
            break;
        N=k;
        for(i=0;i<n;i++)
        for(j=0;j<k;j++)
            scanf("%d",&x[i][j]),x[i][j]%=6;
        for(i=0;i<k;i++)
        for(j=0;j<n;j++)
            scanf("%d",&y[i][j]),y[i][j]%=6;
        clear(a);
        for(i=0;i<k;i++)
        for(j=0;j<k;j++)
        {
            for(ii=0;ii<n;ii++)
            {
                a.m[i][j]+=y[i][ii]*x[ii][j];
            }
            a.m[i][j]%=6;
        }
        ans=matrix_mod(a,n*n-1); //算矩阵A^n次!!!! 
        
          //debug();   
        for(i=0;i<n;i++)
        for(j=0;j<k;j++)
        {
            tmp[i][j]=0;
            for(ii=0;ii<k;ii++)
            {
                tmp[i][j]+=x[i][ii]*ans.m[ii][j];
            }
            tmp[i][j]%=6;
        }
        int ans=0;
        for(i=0;i<n;i++)
        for(j=0;j<n;j++)
        {
            int tt=0;
            for(ii=0;ii<k;ii++)
            {
                tt+=tmp[i][ii]*y[ii][j];
            }
            ans+=(tt%6);
        }
        printf("%d\n",ans);
    }
    return 0;
}
HDU 4968 Improving the GPA

题意:告诉你平均分和科目数  问  你能得到的最大绩点和最小绩点是多少

思路:

作为经常被卡绩点的人  我表示这题想法来的很快- -b

对于最小绩点  那么一定是所有科目都先加到69  然后如果总分还有剩余  就争取把课堆到100  为什么呢  因为69和100最浪费分 - -b

对于最大绩点  一定是所有的都从60开始  然后向85堆  为什么呢  因为反正长5分就是0.5绩点  哪科长都一样  长到85就没有继续的意义了

代码:


#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 100010
typedef __int64 LL;

int t,n,amt;
double mx,mn;

double cal(int f)
{
    if(f>=85) return 2;
    if(f>=80) return 1.5;
    if(f>=75) return 1;
    if(f>=70) return 0.5;
    return 0;
}

int main()
{
    int i,j;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&amt,&n);
        amt*=n;
        i=amt-n*69;
        mn=n*2;
        while(i>0)
        {
            if(i>=31)
            {
                mn+=2;
                i-=31;
            }
            else
            {
                mn+=cal(i+69);
                i=0;
            }
        }
        i=amt-n*60;
        mx=n*2;
        j=0;
        while(i>0&&j<n)
        {
            if(i>=25)
            {
                mx+=2;
                i-=25;
            }
            else
            {
                mx+=cal(i+60);
                i=0;
            }
            j++;
        }
        printf("%.4f %.4f\n",mn/n,mx/n);
    }
    return 0;
}
HDU 4969 Just a Joke

题意:队友看的- -b  大致就是问从圆心起跑的点在时刻保持自己与圆心与圆周上的点共线的情况下能不能在D距离内追到圆周上的点

思路:完全数学题- -b  推出来特别好写…  也不卡精度…

代码:


#include <iostream>
#include <string.h>
#include <stdio.h>
#include <math.h>
using namespace std;

int N;
int MOD=6;
double v1,v2,r,d,k,a;
int main()
{
    int i,j,ii;
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int aa,bb,cc,dd;
        scanf("%d%d%d%d",&aa,&bb,&cc,&dd);
        v1=1.0*aa;v2=1.0*bb;r=1.0*cc;d=1.0*dd;
        a=r*v2/v1;
        r=asin(r/a);
        double ans=a*r;
        if(ans>d)
            printf("Why give up treatment\n");
        else
            printf("Wake up to code\n");
    }
    return 0;
}
HDU 4970 Killing Monsters

题意:又是塔防游戏…  每个塔有个攻击范围有个攻击力  每个怪有个起跑位置有个血量  问几只怪能跑过去

思路:

从后到前扫一遍计算从x点开始到最后一共受多少伤害  (别开脑洞用什么树状数组…就直接扫一遍…我很不理解为啥有人上来就数据结构…)  最后对每个怪判断一下即可

代码:


#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 100010
typedef __int64 LL;

int n,m,ans;
LL sum[N],add[N];

int main()
{
    int i,j,l,r,d;
    LL ad;
    while(~scanf("%d",&n))
    {
        if(!n) break;
        memset(sum,0,sizeof(sum));
        memset(add,0,sizeof(add));
        scanf("%d",&m);
        while(m--)
        {
            scanf("%d%d%d",&l,&r,&d);
            add[r]+=d;
            add[l-1]-=d;
        }
        ad=0;
        for(i=n;i>=1;i--)
        {
            ad+=add[i];
            sum[i]=sum[i+1]+ad;
        }
        //for(i=1;i<=n;i++) printf("%I64d  ",sum[i]);
        ans=0;
        scanf("%d",&m);
        while(m--)
        {
            scanf("%I64d%d",&ad,&d);
            if(sum[d]<ad) ans++;
        }
        printf("%d\n",ans);
    }
    return 0;
}

你可能感兴趣的:(dp,数论,数学,想法,HDU)