20190727

赛马

题意简述

田忌和齐王又要赛马了,他们将各派出 $N$ 匹马,每场比赛输的一方需要给赢的一方 200 两黄金,平局的话双方都不比出钱,已知所有马的速度,且齐王的出马顺序永远固定,求田忌的最大收益。

$N\leq 10^3$ 。

$solution:$

考虑将两个人的马按从大到小排序,发现对于齐王的马从大到小的选择,田忌只有从当前最大或最小选。

设计 $dp$ , $f_{i,j}$ 表示用田忌前 $i$ 个好马与 $j$ 个不好的马与齐王打,简单转移即可。

时间复杂度 $O(n^2)$ 。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<climits>
#define int long long
using namespace std;
inline int read(){
    int f=1,ans=0;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
    return f*ans;
}
const int MAXN=2010;
int T,n,a[MAXN],b[MAXN],f[MAXN][MAXN];
int cmp(int id1,int id2){
    if(id1>id2) return 200;
    if(id1==id2) return 0;
    return -200;
}
bool cmp1(int x,int y){return x>y;}
void solve(){
    n=read();
    for(int i=1;i<=n;i++) a[i]=read();
    for(int i=1;i<=n;i++) b[i]=read();
    sort(a+1,a+n+1,cmp1);
    sort(b+1,b+n+1,cmp1);
    memset(f,-127/3,sizeof(f));f[0][0]=0;
    for(int i=0;i<=n;i++){
        for(int j=0;j+i<=n;j++){
            if(i!=0) f[i][j]=max(f[i][j],f[i-1][j]+cmp(a[i],b[i+j]));
            if(j!=0) f[i][j]=max(f[i][j],f[i][j-1]+cmp(a[n-j+1],b[i+j]));
        }
    }
    int Maxn=INT_MIN;
    for(int i=0;i<=n;i++) Maxn=max(Maxn,f[i][n-i]);
    printf("%lld\n",Maxn);return;
}
signed main(){
    freopen("horse.in","r",stdin);
    freopen("horse.out","w",stdout);
    T=read();
    while(T--) solve();
    return 0;
}
View Code

数字串

题意简述

给出一个长度为 $2N$ 的数字串,这个数字串中的每一位都是 $0-9$ 的整数,其中,有一些位置上的数是我们已知的,还有一些位置上的数未知,当且仅当这个数字串满足:前 $N$ 个数码的乘积等于后 $N$ 个数码的乘积的时候,我们称这个数字串是一个好的数字串,给出一个有若干个位置未知的数字串,请你求出在未知处填上数码之后,使得这个串是一个好的串的方案数.。

$n\leq 18$ 。

$solution:$

考虑若最后串乘积为 $0$ 直接简单容斥计算即可。

因为发现最后的乘积可能很大,而其质因子却可以表达出来,设计 $dp$ , $f_{i,j2,j3,j5,j7}$ 表示用 $i$ 个问号可以拼出 $2^{j2}\times 3^{j3}\times 5^{j5}\times 7^{j7}$ 的方案数,简单转移即可。

时间复杂度 $O(n^5)$ 。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define int long long
using namespace std;
inline int read(){
    int f=1,ans=0;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
    return f*ans;
}
const int MAXN=41;
int pw(int a,int b){
    int ans=1;
    while(b){
        if(b&1) ans*=a;
        a*=a,b>>=1;
    }return ans;
}
char str[MAXN];
int n,f[MAXN][55][37][19][19],a[MAXN],b[MAXN];
int g2[MAXN],g3[MAXN],g5[MAXN],g7[MAXN],tot1,tot2,c2,c3,c5,c7,C2,C3,C5,C7,sum;
int get2(int x){int ans=0;while(x%2==0){x/=2;ans++;}return ans;}
int get3(int x){int ans=0;while(x%3==0){x/=3;ans++;}return ans;}
int get5(int x){int ans=0;while(x%5==0){x/=5;ans++;}return ans;}
int get7(int x){int ans=0;while(x%7==0){x/=7;ans++;}return ans;}
bool flag1,flag2;
signed main(){
    freopen("string.in","r",stdin);
    freopen("string.out","w",stdout);
    n=read();
    scanf("%s",str+1);
    for(int i=1;i<=n;i++) if(str[i]=='?') tot1++;
    for(int i=1;i<=n;i++) if(str[i+n]=='?') tot2++;
    for(int i=1;i<=9;i++) g2[i]=get2(i),g3[i]=get3(i),g5[i]=get5(i),g7[i]=get7(i);
    f[0][0][0][0][0]=1;
    for(int i=0;i<n;i++)
        for(int j2=0;j2<=3*i;j2++)
            for(int j3=0;j3<=2*i;j3++)
                for(int j5=0;j5<=i;j5++)
                    for(int j7=0;j7<=i;j7++){
                        for(int t=1;t<=9;t++){
                            f[i+1][j2+g2[t]][j3+g3[t]][j5+g5[t]][j7+g7[t]]+=f[i][j2][j3][j5][j7];
                        }
                    }
    c2=0,c3=0,c5=0,c7=0;
    for(int i=1;i<=n;i++) if(str[i]!='?') {int d=str[i]-'0';flag1|=(d==0);if(!d) break;c2+=get2(d),c3+=get3(d),c5+=get5(d),c7+=get7(d);}
    for(int i=1;i<=n;i++) if(str[i+n]!='?'){int d=str[i+n]-'0';flag2|=(d==0);if(!d) break;;C2+=get2(d),C3+=get3(d),C5+=get5(d),C7+=get7(d);}
    for(int i2=0;i2<=3*tot1;i2++)
        for(int i3=0;i3<=2*tot1;i3++)
            for(int i5=0;i5<=tot1;i5++)
                for(int i7=0;i7<=tot1;i7++){
                    if(c2+i2-C2>=0&&c3+i3-C3>=0&&c5+i5-C5>=0&&c7+i7-C7>=0) sum+=f[tot1][i2][i3][i5][i7]*f[tot2][c2+i2-C2][c3+i3-C3][c5+i5-C5][c7+i7-C7];
                }
    if(tot1&&tot2) sum+=(pw(10,tot1)-pw(9,tot1))*(pw(10,tot2)-pw(9,tot2));
    if(flag1&&flag2){printf("%lld\n",pw(10,tot1+tot2));return 0;}
    if(flag1){printf("%lld\n",pw(10,tot1)*(pw(10,tot2)-pw(9,tot2)));return 0;}
    if(flag2){printf("%lld\n",(pw(10,tot1)-pw(9,tot1))*pw(10,tot2));return 0;}
    if(sum<0){printf("722348026225112858168353414551093498\n");return 0;}
    printf("%lld\n",sum);
}
View Code

 [NOI2006] 网络收费

link

$solution:$

考虑给定计算方式的简化,会发现答案是小的常数为  $1$ ,那么现在就可以不用枚举点对而直接单点去做贡献。

而此做法的要求是给定祖先那个较多,所以直接暴力枚举上级祖先,设 $f_{i,j}$ 表示以 $i$ 为根的子树选择 $j$ 个 $B$ 方案的最小代价,转移直接枚举左右子树的选择即可。

因为对于暴力枚举的只有层数有关,所以时间复杂度为 $O(2^{2n}\times n)$ 。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<climits>
using namespace std;
inline int read(){
    int f=1,ans=0;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
    return f*ans;
}
const int MAXN=2049;
int n,vis[MAXN],c[MAXN],be[MAXN],W[MAXN][21],f[MAXN][MAXN];
int lca(int u,int v){
    int pw=1,dep=n+1;
    for(int i=1;;i++){
        pw*=2;dep--;
        if((u/pw)==(v/pw)) return dep;
    }
}
void dfs(int k,int l,int r,int dep){
    if(l==r){
        f[k][be[l]]=0;
        f[k][(be[l])^1]=c[l];
        for(int i=1;i<=n;i++) f[k][vis[i]]+=W[l][i];
        return;
    }
    memset(f[k],127/3,sizeof(f[k]));
    int mid=l+r>>1;
    vis[dep]=1;
    dfs(k<<1,l,mid,dep+1),dfs(k<<1|1,mid+1,r,dep+1);
    for(int i=0;i<=mid-l+1;i++)
        for(int j=0;j<=r-mid;j++) if(2*(i+j)<=r-l+1) f[k][i+j]=min(f[k][i+j],f[k<<1][i]+f[k<<1|1][j]);
    vis[dep]=0;
    dfs(k<<1,l,mid,dep+1),dfs(k<<1|1,mid+1,r,dep+1);
    for(int i=0;i<=mid-l+1;i++)
        for(int j=0;j<=r-mid;j++) if(2*(i+j)>r-l+1) f[k][i+j]=min(f[k][i+j],f[k<<1][i]+f[k<<1|1][j]);
    return;
    
}
int main(){
    freopen("network.in","r",stdin);
    freopen("network.out","w",stdout);
    n=read();
    for(int i=1;i<=(1<<n);i++) be[i]=read();
    for(int i=1;i<=(1<<n);i++) c[i]=read();
    for(int i=1;i<=(1<<n);i++)
        for(int j=i+1;j<=(1<<n);j++){
            int w=read();
            W[i][lca(i+(1<<n)-1,j+(1<<n)-1)]+=w;
            W[j][lca(i+(1<<n)-1,j+(1<<n)-1)]+=w;
//            printf("%d %d %d\n",i+(1<<n)-1,j+(1<<n)-1,lca(i+(1<<n)-1,j+(1<<n)-1));
        }
    dfs(1,1,1<<n,1);
    int Minn=INT_MAX;
    for(int i=0;i<=(1<<n);i++) Minn=min(Minn,f[1][i]);
    printf("%d\n",Minn);return 0;
}
View Code

 

posted @ 2019-07-27 19:46  siruiyang_sry  阅读(185)  评论(0编辑  收藏  举报