2017 ACM Amman Collegiate Programming Contest

A - Watching TV

/*
    题意:求出出现次数最多的数字
*/
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int T,n,a[100010];
char s[110];
int main(){
    scanf("%d",&T);
    while(T--){
    	memset(a, 0, sizeof(a));
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
        	int p;
            scanf(" %s %d",&s,&p);
            a[p]++;
        }
        int mx=0,ans;
        for(int i=11111;i<=99999;i++)mx=max(mx,a[i]);
        for(int i=11111;i<=99999;i++)if(mx==a[i]){ans=i;break;}
        printf("%d
", ans);
    }return 0;
} 

B - Longest Prefix

/*
    题目大意:b串可允许多次的任意两个字符交换,问最终a串和b串的最长公共前缀
    题解:记录b串中每个元素的个数,将a串从头到尾扫描匹配,直到b无字符能与a当前位置匹配
*/
#include <cstdio>
#include <algorithm>
#include <cstring> 
using namespace std;
int T,a[26],b[26];
char s[100010],t[100010];
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%s",&s);
        scanf("%s",&t);
        memset(b,0,sizeof(b));
        int n=strlen(t);
        for(int i=0;i<n;i++)b[t[i]-'a']++;
        n=strlen(s);
		int ans=0;
		for(int i=0;i<n;i++){
			if(b[s[i]-'a']){
				ans++;
				b[s[i]-'a']--;
			}else break;
		}
		printf("%d
",ans);
    }
    return 0;
}

C - Lunch Break

/*
    输出最大的数字所在的位置
*/
#include <cstdio>
#include <algorithm>
using namespace std;
int T,a,b,c;
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d%d%d",&a,&b,&c);
        if(c<a&&c<b)puts("Third");
        else if(b<a&&b<c)puts("Second");
        else puts("First");
    }return 0;
}

D - Counting Paths

/*
    组合数取模
*/
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long LL;
const LL P=1000000007;
namespace Comb{
    const int U=200000;
    int f[U+3],rf[U+3];
    LL inv(LL a,LL m){return(a==1?1:inv(m%a,m)*(m-m/a)%m);} 
    void init(){
        f[0]=1;for(int i=1;i<=U;i++)f[i]=(LL)f[i-1]*i%P;
        rf[U]=inv(f[U],P);
        for(int i=U;i;i--)rf[i-1]=(LL)rf[i]*i%P;
    }
    LL C(int n,int m){
        if(m<0||m>n)return 0;
        return (LL)f[n]*rf[m]%P*rf[n-m]%P;
    }
}
int main(){
    int T,a,b;
    Comb::init();
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&a,&b);
        printf("%lld
",Comb::C(a-1,b)*2%P);
    }return 0;
}

E - Car Factory

/*
    输出a+b-1
*/
#include <cstdio>
#include <algorithm>
using namespace std;
int main(){
    int T,a,b;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&a,&b);
        printf("%d
",a+b-1);
    }return 0;
}

F - Cooking Time

/*
    题目大意:每次需要使用材料的时候都要从冰箱中取出,但是冰箱外最多放置k种不同的材料
    所以当取出第k+1种时,必须要放回去一种,当需要使用的材料在外面时就不需要打开冰箱,
    问打开冰箱的最小次数
    题解:我们统计每个数字下一次出现的位置,作为优先队列的关键字,
    我们将放置在外面的材料放入优先队列,每次需要把材料放回冰箱里的时候,
    我们选取下次出现时间最晚的材料即可
*/
#include <cstdio>
#include <algorithm>
#include <queue>
#include <map>
using namespace std;
typedef pair<int,int> P;
priority_queue<P,vector<P> >Q;
const int N=500010;
int T,n,k,a[N],nxt[N],lst[N];
bool inq[N];
int main(){
    scanf("%d",&T);
    while(T--){
        int cnt=0,ans=0;
        scanf("%d%d",&n,&k);
        map<int,int> M;
        for(int i=1;i<=n;i++)lst[i]=n+1;
        for(int i=1;i<=n;i++){
        	scanf("%d",&a[i]);
			if(!M.count(a[i]))M[a[i]]=++cnt;
			inq[M[a[i]]]=0;
        }
        for(int i=1;i<=n;i++)a[i]=M[a[i]];
		while(!Q.empty())Q.pop(); 
        for(int i=n;i;i--)nxt[i]=lst[a[i]],lst[a[i]]=i;
        for(int i=1;i<=n;i++){
            if(inq[a[i]]){Q.push(make_pair(nxt[i],a[i]));continue;}
            if(k){ans++;Q.push(make_pair(nxt[i],a[i]));inq[a[i]]=1;k--;}
            else{
                while(!inq[Q.top().second])Q.pop();
                inq[Q.top().second]=0; Q.pop();
                ans++; Q.push(make_pair(nxt[i],a[i]));
                inq[a[i]]=1;
            }
        }printf("%d
",ans);
    }return 0;
}

G - Super Subarray

/*
    题目大意:给出一个序列,问有多少个区间的区间和是区间每个元素的倍数
    题解:区间和是倍数说明,区间和是区间每个元素的最小公倍数的倍数,
    所以我们n方枚举区间,计算区间和的同时计算公倍数,判断即可。
*/
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int N=2005;
int t,n,a[N];
LL GCD(LL a,LL b){return b==0?a:GCD(b,a%b);}
int main(){
	scanf("%d", &t);
	while(t--){
		long long ma = 0;
		scanf("%d", &n);
		for(int i=1;i<=n;i++){
			scanf("%d",&a[i]);
			ma+=a[i];
		}
		int ans=0;
		for(int i=1;i<=n;i++){
			long long m=0,lcm=1;
			for(int j=i;j<=n;j++){
				m+=a[j];
				lcm=lcm/GCD(a[j],lcm)*a[j];
				if(lcm>ma)break;
				if(m%lcm==0)ans++;
			}
		}printf("%d
",ans);
	}return 0;
} 

H - Palindrome Number

/*
    题目大意:给出一个回文数的长度和数字和,问所能构成的最大的合法回文数,
    如果不存在则输出-1
    题解:基本策略是从外向里贪心,分奇偶考虑,同时特判无解的特殊情况
    以及长度为1时候的特殊情况
*/
#include <cstdio>
#include <algorithm>
#include <cstring> 
using namespace std;
int T,s,l,a[1000100];
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&l,&s);
        memset(a,0,sizeof(a));
        if(9*l<s||s==1&&l>1){puts("-1");continue;}
        if(l%2==0){
            l/=2;
            if(s%2)puts("-1");
            else{
                s/=2;
                for(int i=1;i<=l;i++){
                    a[i]+=s;
                    if(a[i]>9)s=a[i]-9,a[i]=9;
                    else break;
                }
                for(int i=1;i<=l;i++)printf("%d",a[i]);
                for(int i=l;i;i--)printf("%d",a[i]);
                puts("");
            }
        }else{
            l/=2;
            if(s%2)a[l+1]=1;
            s/=2;
            for(int i=1;i<=l;i++){
                a[i]+=s;
                if(a[i]>9)s=a[i]-9,a[i]=9;
                else{s=0;break;}
            }if(s)a[l+1]+=s*2;
            for(int i=1;i<=l+1;i++)printf("%d",a[i]);
            for(int i=l;i;i--)printf("%d",a[i]);
            puts("");
        }
    }
    return 0;
}

I - Rock Piles

/*
    题目大意:给出两堆石子,两人取石子策略为从任意堆取一个石子或者从两堆各取一个石子,
    没有石子可以取的人为输。
    题解:我们发现如果两个石子堆都是偶数则为必败态,因为后手一定可以保持其双偶状态,
    最后到0,0为败,其余状态均可以通过一步操作获得,因此为必胜态。
*/
#include <bits/stdc++.h>
using namespace std;
int t,a,b;
int main(){
	scanf("%d",&t);
	while(t--){
		scanf("%d%d",&a,&b);
		if(a%2==1||b%2==1)puts("hasan");
		else puts("abdullah");
	}return 0;
} 

J - Spilt the String

/*
    题目大意:给出一个带空格的字符串,可以通过空格将这个字符串分段
    使得分段之后的字符串长度相等,问是否能打到要求
    题解:我们在字符串后面补一个空格,同时记录各个空格的位置得到一个数列,
    那么题目转化为是否存在一个首项小于n/2,并且末项为n的等差数列。
*/
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int T,cnt,d[200010],mark[200010];
char s[200010];
int main(){
    scanf("%d",&T);
    gets(s);
    while(T--){
        gets(s+1);
        memset(mark,0,sizeof(mark));
        memset(d,0,sizeof(d));
        int n=strlen(s+1); 
        for(int i=1;i<=n;i++){
            if(s[i]==' ')mark[i]=1;
        }mark[n+1]=1;
        n++; 
        int flag=0;
        for(int i=1;i*2<=n;i++)if(n%i==0&&mark[i]){
        	int t=i,p=1;
        	for(int j=t;j<=n;j=j+t){
        		if(!mark[j]){p=0;break;}
        	}if(p){flag=1;break;}
		} 
		if(flag)puts("YES");
        else puts("NO");
    }
    return 0;
}

K - Two Subarrays

/*
    题目大意:一个区间的价值被定义为这个区间的数字根据下标奇加偶减得到的答案,
    求给出序列的两个不相交的区间的最大的价值差的绝对值
    题解:首先维护正向偶项结尾包含自身的最大最小值和奇项结尾包含自身的最大最小值,
    对这些最大最小值求一个前缀极值,就是前缀最大最小值,
    考虑反向求前缀极值,因为反向的奇偶项特殊性导致最靠前的一项永远是奇数项,
    因此只要维护奇数向的包含当前位置的后缀极值即可,同理,求完前缀极值就是前缀最大最小
    对于题目要求的不相交区间,考虑枚举端点,那么端点处分割的前后缀极值均已知,
    可组合计算极值更新答案。
*/
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long LL;
const int N=100010;
int n,T;
LL a[N],mx[2][N],mn[2][N],l[2][N],r[2][N];
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        memset(mx,0,sizeof(mx));
        memset(mn,0,sizeof(mn));
        memset(l,0,sizeof(l));
        memset(r,0,sizeof(r));
        memset(a,0,sizeof(a));
        for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
        mx[1][1]=a[1]; 
        mn[1][1]=a[1]; 
        for(int i=2;i<=n;i++){
            mx[1][i]=max(a[i],mx[0][i-1]+a[i]);
            mx[0][i]=max(mx[1][i-1]-a[i],a[i-1]-a[i]);
            mn[1][i]=min(a[i],mn[0][i-1]+a[i]);
            mn[0][i]=min(mn[1][i-1]-a[i],a[i-1]-a[i]);
        }l[0][1]=a[1]; l[1][1]=a[1];
        for(int i=2;i<=n;i++){
            l[0][i]=max(l[0][i-1],mx[0][i]);
            l[0][i]=max(l[0][i],mx[1][i]);
            l[1][i]=min(l[1][i-1],mn[0][i]);
            l[1][i]=min(l[1][i],mn[1][i]);
        }
        for(int i=n;i;i--){
            r[0][i]=max(a[i],a[i]-r[1][i+1]);
            r[1][i]=min(a[i],a[i]-r[0][i+1]);
        }
        for(int i=n-1;i;i--){
            r[0][i]=max(r[0][i],r[0][i+1]);
            r[1][i]=min(r[1][i],r[1][i+1]);
        }
        LL ans=abs(a[1]-a[2]);
        for(int i=1;i<n;i++){
            ans=max(ans,abs(l[0][i]-r[1][i+1]));
            ans=max(ans,abs(l[1][i]-r[0][i+1]));
        }
        printf("%lld
",ans);
    }
    return 0;
}

L - The Shortest Path

/*
    题目大意:给出一个有向图,求出其全局最短路,如果存在负环,则输出-inf
    题解:首先,如果图中不存在负权边,那么最短路即为最小正权边,
    否则利用spfa判断负环,首先将所有点入队,等价于建立超级源点,
    然后依次出队进行运算,若某点入队n次或者其距离值小于距离最小可能值,
    说明存在负环,距离最小可能值通过输入时累加负权边获得。
*/
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long LL;
const int N=2010,M=5010;
int v[M<<1],w[M<<1],g[N],nxt[M<<1],cnt[N],in[N],ed;
LL d[N],dsum;
int q[N*N]; int h,t;
void add_edge(int x,int y,int z){
    v[++ed]=y; w[ed]=z; nxt[ed]=g[x]; g[x]=ed;
}
void add(int x,LL y){
    if(y>=d[x])return;d[x]=y;
    if(!in[x]){
        in[x]=1; cnt[x]++;
        q[++t]=x; 
    }
}
int T,n,m;
bool spfa(){
    int i,x; t=0;
    for(i=h=1;i<=n;i++)d[i]=0,in[i]=1,cnt[i]=1,q[++t]=i;
    while(h!=t+1){
		for(i=g[x=q[h++]],in[x]=0;i;i=nxt[i]){
        	add(v[i],d[x]+w[i]);
        	if(cnt[v[i]]>n||d[v[i]]<dsum)return 1;
        }
    }return 0;
}
int main(){
    scanf("%d",&T);
    while(T--){
    	dsum=0;
        scanf("%d%d",&n,&m);
        memset(g,ed=0,sizeof(g));
        LL ans=0x3f3f3f3f3f3f3f3f;        
        for(int i=1;i<=m;i++){
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            add_edge(x,y,z);
            ans=min(1LL*z,ans);
            if(z<0)dsum+=z;
        }
        if(ans>=0)printf("%d
",ans);
        else{
            if(spfa())puts("-inf");
            else{
                for(int i=1;i<=n;i++)ans=min(ans,d[i]);
                printf("%d
",ans);
            }
        }
    }return 0;
}

M - Restore Points

/*
    题目大意:将至多18个包含0在内的非负整数两两求差,得到差值序列,
    现已知差值序列,求原数列的可能值
    题解:首先我们可以确定最大的数值mx,那么除去0,需要确立剩余16个数字,
    我们将所有差值标记c[diff]++,如果一个差值diff满足c[diff]=1&&c[mx-diff]=1,
    那么说明要么存在一个点在diff位置,要么在mx-diff位置,
    这是一个二叉分治的搜索操作,每次确定一个点,就将其与所有确定点的差值序列
    从c数组中删去,当确定其不合法时回溯,因为要保证解存在,
    所以我们可以通过下个点的两个位置放置均不合法来判定当前放置点不合法。
*/
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int T,n,m,flag=0,a[400],b[20],c[1000010];
bool cmp(int a,int b){return a>b;} 
void dfs(int x,int pos){
    int i=pos,p=1;
	if(x>n){flag=1;return;}
	for(;i<m;i++)if(c[a[i]]&&c[a[0]-a[i]])break;
	for(int j=1;j<x;j++)if(--c[abs(a[i]-b[j])]<0)p=0;
	if(p){b[x]=a[i];dfs(x+1,pos+1);if(flag)return;}
    for(int j=1;j<x;j++)c[abs(a[i]-b[j])]++; p=1;
	for(int j=1;j<x;j++)if(--c[abs(a[0]-a[i]-b[j])]<0)p=0;
	if(p){b[x]=a[0]-a[i];dfs(x+1,pos+1);if(flag)return;}
	for(int j=1;j<x;j++)c[abs(a[0]-a[i]-b[j])]++;
}
int main(){
    scanf("%d",&T);
    while(T--){
        memset(c,0,sizeof(c));
        scanf("%d",&n);
        m=n*(n-1)/2;
        for(int i=0;i<m;i++){
            scanf("%d",&a[i]);
            c[a[i]]++;
        }sort(a,a+m,cmp);
        b[1]=0; b[2]=a[0];
        c[a[0]]--; flag=0; dfs(3,1);
        sort(b+1,b+n+1);
        for(int i=1;i<n;i++)printf("%d ",b[i]);
        printf("%d
",b[n]);
    }return 0;
}
原文地址:https://www.cnblogs.com/forever97/p/gym101498.html