2020 CCPC Wannafly Winter Camp Day6 部分题解

测试地址:牛客竞赛

C 酒馆战棋

题意简述
了解炉石传说的应该不难理解题意。太长了还是去看原题面吧/

解题思路
模拟题,需要注意我方随从是从左到右依次进攻的,若忽略这点会浪费很多时间。
Code
队友的代码:

#include 
#include 
#include 
#include 
#include 
#define ll long long
using namespace std;
 
const int N = 1007;
int n,cas,a,b,c,d;
char bk[N];
int solve_max(int n, int a, int b, int c, int d){
    int res=0;
    for (int i=0;i<n;++i){
        if (bk[i]=='1'){
            if (c){//嘲讽
                c--; res++; continue;
            }
            if (d){//圣盾嘲讽
                d--; c++; continue;
            }
            if (a){//普通
                a--; res++; continue;
            }
            if (b){//圣盾
                b--; a++; continue;
            }
        }else{
            if (d){//不是无穷大时先攻击 圣盾嘲讽
                d--; c++; continue;
            }
            if (c){//嘲讽
                continue;
            }
            if (b){//圣盾
                b--; a++; continue;
            }
        }
    }
    return res;
}
 
int solve_min(int n, int a, int b, int c, int d){
    int res=0;
    for (int i=0;i<n;++i){
        if (bk[i]=='1'){
            if (d){
                d--; c++; continue;
            }
            if (c){
                c--; res++; continue;
            }
            if (b){
                b--; a++; continue;
            }
            if (a){
                a--; res++; continue;
            }
        }else{
            if (c) continue;//不是无穷大时先攻击嘲讽
            if (d){
                d--; c++; continue;
            }
            if (a)  continue;
            if (b){
                b--; a++; continue;
            }
        }
    }
    return res;
}
int main(){
    scanf("%d",&cas);
    while(cas--){
        int cnt = 0;
        scanf("%d%d%d%d%d",&n,&a,&b,&c,&d);
        scanf("%s",bk);
        printf("%d %d\n",solve_max(n,a,b,c,d),solve_min(n,a,b,c,d));
    }
    return 0;
}

G 单调栈

题意简述

2020 CCPC Wannafly Winter Camp Day6 部分题解_第1张图片
解题思路
俺没看这题,队友说是贪心思路。
是先从右向左把f[i]小的先确定 把那些f[i]为-1的 从小到大依次填进去 保证字典序最小。
Code

#include 
#include 
#include 
#include 
#include 
#define ll long long
using namespace std;
 
const int N=107;
int n,a[N],ans[N],cas,cnt = 0,cur = 1;
void solve(){
    cnt=0,cur=1;
    while(cnt<n){
        for(int i=n;i;--i)
            if(a[i]==cur) ans[i]=++cnt;
        for(int i=1;i<=n;++i)
            if(a[i]==-1){
                a[i]=cur;
                ans[i]=++cnt;
                break;
            }
            else if(a[i]==cur) break;
        ++cur;
    }
    for(int i=1;i<n;++i) printf("%d ",ans[i]);
    printf("%d\n",ans[n]);
}
int main(){
    //freopen("data.in","r",stdin);
    scanf("%d",&cas);
    while(cas--){
        scanf("%d",&n);
        for(int i=1;i<=n;++i) scanf("%d",&a[i]);
        solve();
    }
    return 0;
}

K 最大权值排列

题意简述
1~n 共 n 个数,请问怎么排列才能使得所有子区间的平均值 之和最大?
解题思路
易得每个位置上的数被计算的次数是不一样的,容易想象,因为是求平均数之和最大,肯定是越大的数计算次数越多越好,可以举个例子写个小证明。通过打表发现对于一个序列,越靠近中间位置的数被计算的次数越多,所以尽量把大的数放在中间即可。

Code

#include
using namespace std;
const int N = 1e5+10;
int a[N],n;
int main(){
	scanf("%d",&n); 
	int t = 1,p = 1,q = n;
	while(t <= n){
		a[p++] = t++;
		if(t > n) break;
		a[q--] = t++;
	}
	for(int i = 1;i <= n;i++) cout << a[i] << " ";
	return 0;
}

L 你吓到我的马了.jpg

题意简述
2020 CCPC Wannafly Winter Camp Day6 部分题解_第2张图片
解题思路
队友说一个 bfs

Code

#include 
#include 
#include 
#define ll long long
 
const int N = 107;
int n,m,sx,sy;
char map[N][N];
int ans[N][N];
int bk[N][N];
int dir[][2] = {{-1,2},{1,2},{2,1},{2,-1},{1,-2},{-1,-2},{-2,-1},{-2,1}};
int bar[][2] = {{0,1},{1,0},{0,-1},{-1,0}};
struct Node{
    int x,y,step;
}node;
void solve(){
    std::queue<Node> que;
    node.x = sx,node.y = sy,node.step = 0;
    que.push(node);
    bk[sx][sy] = 0;
    while(que.size()){
        Node frt =  que.front();
        que.pop();
        int x = frt.x,y = frt.y,step = frt.step;
        //printf("x= %d, y = %d\n",x,y);
        for(int i = 0; i < 8; ++i){
            int xt = x+dir[i][0],yt = y+dir[i][1];
            int xb = x+bar[i/2][0],yb = y+bar[i/2][1];
            if(xt < 1 || xt > n || yt < 1|| yt > m) continue;
            if(map[xt][yt] != 'X' && map[xb][yb] != 'X' && bk[xt][yt] == -1){
                bk[xt][yt] = step+1;
                node.x = xt,node.y = yt,node.step = step+1;
                que.push(node);
            }
                 
        }
    }
    for(int i = 1; i <= n; ++i){
        for(int j = 1; j <= m; ++j)
            printf("%d ",bk[i][j]);
        printf("\n");
    }
 
         
}  
int main(){
    //freopen("data.in","r",stdin);
    scanf("%d %d",&n,&m);
    for(int i = 1; i <= n; ++i){
        getchar();
        for(int j = 1; j <= m; ++j){
            scanf("%c",&map[i][j]);
            if(map[i][j] == 'M') sx=i,sy=j;
            else bk[i][j] = -1;
        }
         
    }
    solve();
 
     
    return 0;
}

N 合并!

题意简述
给定 n 堆石子,第 i 堆有 ai 个石子。合并第 i 堆和第 j 堆的得分是 ai * aj,合并后新堆有 ai+aj 个石子,请问将这 n 个石子合并成一堆的最大得分。

解题思路
这题 n <= 2000,朴素的动态规划需要 O(n^3) ,超时。我猜它可以用四边形不等式优化,结果果然可以,于是没有证明直接套用。

Code

#include
using namespace std;
typedef long long ll;
const int N = 2000+10;
ll f[N][N],a[N],sum[N];
int n,s[N][N];
inline ll w(int i,int k,int j){
	return (sum[k]-sum[i-1])*(sum[j]-sum[k]);
}
void solve(){
	for(int i = 1;i <= n;i++) sum[i] = sum[i-1] + a[i],s[i][i] = i;
	for(int len = 2;len <= n;len++)
		for(int i = 1;i <= n-len+1;i++){
			int j = i+len-1;
			for(int k = s[i][j-1];k < s[i+1][j];k++) 
				if(f[i][j] < f[i][k]+f[k+1][j]+w(i,k,j)){
					f[i][j] = f[i][k]+f[k+1][j]+w(i,k,j);
					s[i][j] = k;
				}
		}
	printf("%lld\n",f[1][n]);
}
int main(){
	scanf("%d",&n);
	for(int i = 1;i <= n;i++) scanf("%lld",a+i);
	solve();
	return 0;
}

你可能感兴趣的:(假期练习)