湖南雅礼培训 1.2

…衆ロ難τιáo~ 提交于 2020-03-07 08:29:50

模拟赛

串(string)

【题目描述】
给定一个由小写字母组成的字符串 s,每次你可以删去它的一个非回文子串,
求删成空串的最小次数。
【输入数据】
第一行一个整数 t 表示数据组数。
每组数据第一行一个整数 n 表示字符串长度,第二行一个字符串 s。
【输出数据】
每组数据输出一行一个整数表示答案,如果无法删成空串输出-1。
【样例输入】
2
7
abcdcba
3
xxx
【样例输出】
2
-1
【样例解释】
对于第一个样例,一种最优方案为 abcdcba->adcba->空串。
【数据范围】
对于 30%的数据,n<=10。
对于 60%的数据,n<=100。
对于 100%的数据,t<=20,n<=10^5。

#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 110
#define INF 0x7fffffff
using namespace std;
int ans;
struct node{
    string s;
    int len;
};
void solve(node now,int step){//已经进行了step次 
    if(step>=ans)return;
    for(int l=0;l<now.len;l++){
        for(int r=l+1;r<now.len;r++){
            int ll=l,rr=r;bool flag=0;
            while(ll<=rr){
                if(now.s[ll]!=now.s[rr]){flag=1;break;}
                else ll++,rr--;
            }
            if(flag==0)continue;
            node nxt;nxt.s="";nxt.len=0;
            for(int i=0;i<l;i++)nxt.s+=now.s[i],nxt.len++;
            for(int i=r+1;i<now.len;i++)nxt.s+=now.s[i],nxt.len++;
            if(nxt.len==0){
                ans=min(ans,step+1);
                return;
            }
            solve(nxt,step+1);
        }
    }
}
int main(){
//    freopen("Cola.txt","r",stdin);
    freopen("string.in","r",stdin);freopen("string.out","w",stdout);
    int T;
    scanf("%d",&T);
    while(T--){
        ans=INF;
        node str;
        scanf("%d",&str.len);cin>>str.s;
        solve(str,0);
        if(ans<INF)printf("%d\n",ans);
        else puts("-1");
    }
    return 0;
}
30分 暴力
/*
    首先如果s不是回文串答案为1。
    考虑对于1<=i<n,是否都满足s[1..i]和s[i+1..n]至少有一个是回文的。如果不满足那么答案为2。
    如果满足的话,如果s[1]=s[2],那么s只会形如aaaaa或aaabaaa;如果s[1]!=s[2],那么s只会形如abababa。这三种情况都是无解的。
*/
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int n;
char s[100010];
int main(){
    freopen("string10.in","r",stdin);
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d%s",&n,s+1);
        int l=1,r=n;
        bool flag=0;
        while(l<=r){
            if(s[l]!=s[r]){flag=1;break;}//不是回文串 
            l++,r--;
        }
        if(flag){puts("1");continue;}
        for(int i=1;i<=n;i++){//枚举中间点 
            l=1,r=i;
            bool flag1=0,flag2=0;
            while(l<=r){
                if(s[l]!=s[r]){flag1=1;break;}
                l++,r--;
            }
            if(flag1==0)continue;
            l=i+1,r=n;
            while(l<=r){
                if(s[l]!=s[r]){flag2=1;break;}
                l++;r--;
            }
            if(flag1&&flag2){flag=1;puts("2");break;}
        }
        if(!flag)puts("-1");
    }
    return 0;
}
60分 结论+暴力判断是否回文
/*
    不是直接求得回文串 
    而是根据下面的三种情况得到的
    如果不是下面三种情况
    就肯定是满足条件的
    如果满足的话,如果s[1]=s[2],那么s只会形如aaaaa或aaabaaa;如果s[1]!=s[2],那么s只会形如abababa。这三种情况都是无解的。
    也就是其它情况都是输出2
*/
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int n;
char s[100010];
int main(){
    freopen("string9.in","r",stdin);
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d%s",&n,s+1);
        int l=1,r=n;
        bool flag=0;
        while(l<=r){
            if(s[l]!=s[r]){flag=1;break;}//不是回文串 
            l++,r--;
        }
        if(flag){puts("1");continue;}
        bool f1=0,f2=0,f3=0;
        for(int i=1;i<=n;i++){
            if(i>1&&s[i]!=s[i-1])f1=1;//不满足第一个条件 
            if(i>2&&s[i]!=s[i-2])f2=1;//不满足第二个条件 
        }
        int mid=(n/2)+1;
        for(int i=2;i<mid;i++)if(s[i]!=s[i-1])f3=1;
        for(int i=mid+2;i<=n;i++)if(s[i]!=s[i-1])f3=1;
        if(f1&&f2&&f3)puts("2");//三个条件都不满足 
        else puts("-1");
    }
    return 0;
}
100分

 


变量(variable)


【题目描述】
有 n 个变量 w[1]~w[n],每个变量可以取 W 或-W。
有 p 个式子,形如 Hi=ai|w[xi]-w[yi]|+bi|w[yi]-w[zi]|+ci|w[zi]-w[xi]|
+di(w[xi]-w[yi])+ei(w[yi]-w[zi])+fi(w[zi]-w[xi])。
有 q 个条件,形如 w[x]<=w[y]或 w[x]=w[y]或 w[x]<w[y]。
最小化 sigma(wi)+sigma(Hi)。
【输入数据】
第一行一个整数 t 表示数据组数。
每组数据第一行四个整数 n,W,p,q 表示节点数。
接下来 p 行每行九个整数 xi,yi,zi,ai,bi,ci,di,ei,fi。
接下来 q 行每行三个整数 x,y,r。
r=0 表示 w[x]<=w[y];r=1 表示 w[x]=w[y];r=2 表示 w[x]<w[y]。
保证存在方案。
【输出数据】
每组数据输出一行一个整数表示 sigma(wi)+sigma(Hi)的最小值。
【样例输入】
1
3 1 1 1
1 2 3 1 1 1 1 1 1
1 2 2
【样例输出】
3
【数据范围】
对于 30%的数据,n<=15,p,q<=20。
对于 100%的数据,t<=10,n<=500,p,q<=1000,1<=W<=10^6,
0<=ai,bi,ci,di,ei,fi<=1000。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#define maxn 510
#define INF 0x7fffffff
using namespace std;
int ans,n,W,p,q,link[maxn][maxn],w[maxn];
struct node{
    int x,y,z,a,b,c,d,e,f;
}qu[maxn];
struct Node{
    int x,y,v;
}r[maxn];
bool check(int sta){
    for(int i=1;i<=n;i++){
        w[i]=sta&(1<<(i-1));
        if(w[i]!=0)w[i]=W;
        else w[i]=-W;
    }
    for(int i=1;i<=q;i++){
        if(r[i].v==0){
            if(w[r[i].x]>w[r[i].y])return 0;
        }
        else if(r[i].v==1){
            if(w[r[i].x]!=w[r[i].y])return 0;
        }
        else if(r[i].v==2){
            if(w[r[i].x]>=w[r[i].y])return 0;
        }
    }
    return 1;
}
int main(){
//    freopen("Cola.txt","r",stdin);
    freopen("variable.in","r",stdin);freopen("variable.out","w",stdout);
    int T;
    scanf("%d",&T);
    while(T--){
        ans=INF;
        scanf("%d%d%d%d",&n,&W,&p,&q);
        for(int i=1;i<=p;i++)scanf("%d%d%d%d%d%d%d%d%d",&qu[i].x,&qu[i].y,&qu[i].z,&qu[i].a,&qu[i].b,&qu[i].c,&qu[i].d,&qu[i].e,&qu[i].f);
        for(int i=1;i<=q;i++)scanf("%d%d%d",&r[i].x,&r[i].y,&r[i].v);
        for(int sta=0;sta<(1<<n);sta++){
            if(!check(sta))continue;
            int s1=0,s2=0;
            for(int i=1;i<=n;i++)s1+=w[i];
            for(int i=1;i<=p;i++){
                s2+=qu[i].a*abs(w[qu[i].x]-w[qu[i].y])+qu[i].b*abs(w[qu[i].y]-w[qu[i].z])+qu[i].c*abs(w[qu[i].z]-w[qu[i].x]);
                s2+=qu[i].d*(w[qu[i].x]-w[qu[i].y])+qu[i].e*(w[qu[i].y]-w[qu[i].z])+qu[i].f*(w[qu[i].z]-w[qu[i].x]);
            }
            ans=min(ans,s1+s2);
        }
        printf("%d\n",ans);
    }
    return 0;
}
10分 暴力,开long long 能得30分
/*
    每个变量建一个点i,S向i连边,i向T连边。割这两条边分别表示取-W和取W。对于x[i]*w[i],讨论正负往其中一条边上加权值。
    对于y[j]*|w[a[j]]-w[b[j]]|,在a[j]和b[j]间连权值为2y[j]的无向边。
    对于限制,wa<wb等价于wa=-W且wb=W;wa=wb就在a和b间连权值为正无穷的无向边;wa<=wb就a向b连权值为正无穷的单向边。最后求最小割即可。
    时间复杂度O(t*maxflow(n,n+p+q))
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<queue>
#define INF 0x3f3f3f3f
using namespace std;
int n,m,s,t,op,oq,head[10005],num=1,dis[10005],vis[10005],mp[510][510],val[510],cur[10005];
long long w;
struct node{
    int to,pre,cap,flow;
}e[100005];
long long qread(){
    int j=1;long long i=0;
    char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')j=-1;ch=getchar();}
    while(ch<='9'&&ch>='0'){i=i*10+ch-'0';ch=getchar();}
    return i*j;
}
void Insert(int from,int to,int v){
    e[++num].to=to;e[num].cap=v;e[num].flow=0;e[num].pre=head[from];head[from]=num;
    e[++num].to=from;e[num].cap=0;e[num].flow=0;e[num].pre=head[to];head[to]=num;
}
int bfs(){
    for(int i=1;i<=t;i++)dis[i]=INF,vis[i]=0,cur[i]=head[i];
    queue<int>q;q.push(s);dis[s]=0;vis[s]=1;q.push(s);
    while(!q.empty()){
        int now=q.front();q.pop();
        for(int i=head[now];i;i=e[i].pre){
            int to=e[i].to;
            if(!vis[to]&&e[i].cap-e[i].flow>0){
                vis[to]=1;dis[to]=dis[now]+1;q.push(to);
            }
        }
    }
    return vis[t];
}
int dfs(int now,int flow){
    if(now==t||flow==0)return flow;
    int delta,rest=0;
    for(int &i=cur[now];i;i=e[i].pre){
        int to=e[i].to;
        if(dis[now]+1==dis[to]){
            delta=dfs(to,min(flow,e[i].cap-e[i].flow));
            if(delta>0){
                e[i].flow+=delta;
                e[i^1].flow-=delta;
                rest+=delta;
                flow-=delta;
                if(flow==0)break;
            }
        }
    }
    return rest;
}
int dinic(){
    int maxflow=0;
    while(bfs())maxflow+=dfs(s,INF);
    return maxflow;
}
int main(){
    freopen("variable9.in","r",stdin);
    int T;scanf("%d",&T);
    while(T--){
        scanf("%d",&n);w=qread();scanf("%d%d",&op,&oq);
        memset(head,0,sizeof(head));num=1;
        memset(mp,0,sizeof(mp));
        memset(val,0,sizeof(val));
        for(int i=1;i<=n;i++)val[i]=1;
        for(int i=1;i<=op;i++){
            int xi,yi,zi,ai,bi,ci,di,ei,fi;
            scanf("%d%d%d%d%d%d%d%d%d",&xi,&yi,&zi,&ai,&bi,&ci,&di,&ei,&fi);
            val[xi]+=di-fi;
            val[yi]+=ei-di;
            val[zi]+=fi-ei;
            if(xi<yi)mp[xi][yi]+=ai;else mp[yi][xi]+=ai;
            if(yi<zi)mp[yi][zi]+=bi;else mp[zi][yi]+=bi;
            if(zi<xi)mp[zi][xi]+=ci;else mp[xi][zi]+=ci;
        }
        int mx=0;
        for(int i=1;i<=n;i++)mx=max(mx,abs(val[i]));
        mx++;
        s=2*n+1;t=2*n+2;
        for(int i=1;i<=n;i++){
            Insert(s,i,mx+val[i]);
            Insert(i,i+n,INF);
            Insert(i+n,t,mx-val[i]);
        }
        for(int i=1;i<=n;i++)
            for(int j=i+1;j<=n;j++){
                if(mp[i][j]==0)continue;
                Insert(i+n,j,2*mp[i][j]);
                Insert(j+n,i,2*mp[i][j]);
            }
        for(int i=1;i<=oq;i++){
            int xi,yi,zi;
            scanf("%d%d%d",&xi,&yi,&zi);
            if(zi==0)Insert(yi+n,xi,INF);
            if(zi==1)Insert(xi+n,yi,INF),Insert(yi+n,xi,INF);
            if(zi==2)Insert(s,xi,INF),Insert(yi+n,t,INF);
        }
        long long ans=1LL*(dinic()-mx*n)*w;
        cout<<ans<<endl;
    }
    return 0;
}
100分 最小割

 


取石子(stone)


【题目描述】
有 n 堆石子,第 i 堆有 xi 个。
Alice 和 Bob 轮流取石子(先后手未定),Alice 每次从一堆中取走 a 个,Bob
每次从一堆中取走 b 个,无法操作者输。
不难发现只会有四种情况:Alice 必胜;Bob 必胜;先手必胜;后手必胜。
你需要选定若干堆石子(共有 2^n 种方案),Alice 和 Bob 只能在你选出的堆
中取,问以上四种情况对应的方案数。
【输入数据】
第一行三个整数 n,a,b,第二行 n 个整数 x1~xn。
【输出数据】
一行四个整数,分别表示 Alice 必胜、Bob 必胜、先手必胜和后手必胜的方
案数,对 10^9+7 取模。
【样例输入】
2 2 3
2 3
【样例输出】
2 0 1 1
【样例解释】
选定空集时后手必胜,选定{2}时 Alice 必胜,选定{3}时先手必胜,选定{2,3}时 Alice
必胜。
【数据范围】
对于 10%的数据,n,xi<=5。
对于 50%的数据,n<=20。
对于另外 10%的数据,a=b。
对于又另外 20%的数据,a=1。
对于 100%的数据,1<=n<=100000,1<=a,b,xi<=10^9。

/*
    不妨假设a<b。
    每堆石子先对a+b取模,然后可以分为4种:
    (1)    xi<a,没用。
    (2)    a<=xi<b,只要存在则a必胜。
    (3)    b<=xi<2a,只和奇偶性有关。
    (4)    2a<=xi,存在至少2个则a必胜,存在1个且(3)为偶数则先手必胜,存在1个且(3)为奇数则a必胜,不存在且(3)为奇数则先手必胜,不存在且(3)为偶数则后手必胜。
    时间复杂度O(n)
*/
#include<bits/stdc++.h>
#define L long long
using namespace std;
const int q=1000000007;
int n,a,b,x[4],f[4];
inline int power(int a,int b)
{
    if(!b)
      return 1;
    int c=power(a,b>>1);
    c=(L)c*c%q;
    if(b&1)
      c=(L)c*a%q;
    return c;
}
int main()
{
    freopen("stone.in","r",stdin);
    freopen("stone.out","w",stdout);
    int i,j,k=0;
    scanf("%d%d%d",&n,&a,&b);
    if(a>b)
      swap(a,b),k=1;
    for(i=1;i<=n;i++)
      {
       scanf("%d",&j);
       j%=a+b;
       x[(j>=a)+(j>=b)+(j>=b && j>=2*a)]++;
      }
    f[0]=((L)(power(2,x[1])-1)*power(2,x[2]+x[3])+(L)(power(2,x[3])-x[3]-1+q)*power(2,x[2])+(L)x[3]*(x[2]?power(2,x[2]-1):0))%q;
    f[2]=((x[2]?power(2,x[2]-1):0)+(L)x[3]*(x[2]?power(2,x[2]-1):1))%q;
    f[3]=(x[2]?power(2,x[2]-1):1);
    for(i=0;i<4;i++)
      f[i]=(L)f[i]*power(2,x[0])%q;
    if(k)
      swap(f[0],f[1]);
    for(i=0;i<4;i++)
      printf("%d ",f[i]);
    printf("\n");
    return 0;
}
100分

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!