XCPC集训十题解

A - Glutton Takahashi

题意:

给定 n n n个字符串,问是否有两个相邻的 sweet

解题思路:

遍历判断当前字符串与上一个字符串是否都为sweet即可。

代码:



B - Grid Walk

题意:

给定一个二维网格,有障碍物,有空地,给定初始位置。
给定移动操作,若移动目标位置为空地则移动,否则留在原地。
问最终位置。

解题思路:

模拟即可

代码:

C - Minimum Glutton

题意:

n n n个食物,有咸度和甜度。安排一个吃的顺序,使得吃的食物尽可能少,且一旦咸度 > x >x >x或甜度 > y >y >y就停下来不吃。

解题思路:

代码:

在这里插入代码片

D - K-th Nearest

题意:

一维坐标,给定$n个点,依次回答 q q q个询问。每个询问给定一个位置,问距离该位置第 k k k近的点的距离是多少。

解题思路:

代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;

void solve()
{
    int n, q;
    cin >> n >> q;

    vector<int> a(n + 1);
    for (int i = 1; i <= n; ++i)
        cin >> a[i];

    sort(a.begin() + 1, a.end());

    int b, k;
    while (q--)
    {
        // 二分枚举距离
        cin >> b >> k;
        int l = 0, r = 2e8, mid;    
        while (l < r)
        {
            mid = l + r >> 1;
            // 如果不用得到具体下标,则可以用 auto,这样就可以免得减去起始位置了
            auto lb = lower_bound(a.begin() + 1, a.end(), b - mid);
            auto rb = upper_bound(a.begin() + 1, a.end(), b + mid);
            int all = rb - lb;
            if (all >= k)
                r = mid;
            else
                l = mid + 1;
        }
        cout << l << '\n';
    }
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int t = 1;
    // cin >> t;
    while (t--)
        solve();

    return 0;
}

E - Maximum Glutton

题意:

解题思路:

代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;

void solve()
{
    int n, x, y;
    cin >> n >> x >> y;

    int a, b;
    vector<vector<int>> dp(n + 1, vector<int>(x + 1, 0x3f3f3f3f));
    for (int i = 0; i <= x; ++i)
        dp[0][i] = 0;

    for (int i = 1; i <= n; ++i)
    {
        cin >> a >> b;
        for (int j = i; j >= 1; --j)
            for (int k = x; k >= a; --k)
                dp[j][k] = min(dp[j][k], dp[j - 1][k - a] + b);
    }

    for (int j = n - 1; j >= 0; --j)
        if (dp[j][x] <= y)
        {
            cout << j + 1;
            return;
        }
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int t = 1;
    // cin >> t;
    while (t--)
        solve();

    return 0;
}
在这里插入代码片

F - Range Connect MST

题意:

解题思路:

代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;

bool cmp(const vector<int> &x, const vector<int> &y)
{
    return x[2] < y[2];
}

int fa[200010];
int find(int x)
{
    if (x == fa[x])
        return x;
    return fa[x] = find(fa[x]);
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n, q;
    cin >> n >> q;

    for (int i = 1; i <= n; ++i)
        fa[i] = i;

    vector<vector<int>> a(q, vector<int>(3));
    for (int i = 0; i < q; ++i)
        cin >> a[i][0] >> a[i][1] >> a[i][2];
    sort(a.begin(), a.end(), cmp);

    int ans = 0;
    int cnt = 0;
    for (int i = 0; i < q; ++i)
    {
        int l = a[i][0];
        int r = a[i][1];
        int c = a[i][2];
        ans += c;
        for (int j = find(l) + 1; j <= r; j = find(j) + 1)
        {
            cnt++;
            ans += c;
            fa[j - 1] = j;
        }
    }

    if (cnt != n - 1)
        cout << -1;
    else
        cout << ans;
    return 0;
}
在这里插入代码片

G - Last Major City

题意:

解题思路:

代码:

H - Spoiler

题意:

给定一个字符串,包含两个|,将|和两个|之间的字符消去。

解题思路:

模拟即可

代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e5+100,mod=998244353;
typedef long long ll;
typedef pair<pair<int,int>,int> PII;
int T;
void solve()
{
    string op;
    cin>>op;
    int l=-1,r=-1;
    for(int i=0;i<op.size();i++)
    {
    	if(l==-1&&op[i]=='|') l=i;
    	else if(op[i]=='|'&&l!=-1) r=i;
    }
    for(int i=0;i<op.size();i++)
    {
    	if(i>=l&&i<=r) continue;
    	else cout<<op[i];
    }
}
signed main()
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    T=1;
    //cin>>T;
    while(T--)
     {
         solve();
     }
     return 0;
} 

I - Delimiter

题意:

给定 n n n个数,倒序输出。

解题思路:

用数组储存,之后直接倒着输出

代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e5+100,mod=998244353;
typedef long long ll;
typedef pair<pair<int,int>,int> PII;
int T;
void solve()
{
    vector<int> res;
    int x;
    while(1)
    {
    	cin>>x;
    	res.push_back(x);
    	if(x==0) break;
    }
    reverse(res.begin(),res.end());
    for(auto i : res) cout<<i<<endl;
}
signed main()
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    T=1;
    //cin>>T;
    while(T--)
     {
         solve();
     }
     return 0;
} 

J - A+B+C

题意:

给定三个数组 a , b , c a,b,c a,b,c,回答 q q q次询问。
每次询问,给定 x x x,问能否从三个数组各选一个数,其和为 x x x

解题思路:

由于数据范围很小,可以直接预处理出能组合出来的数,最后判断即可

代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e5+100,mod=998244353;
typedef long long ll;
typedef pair<pair<int,int>,int> PII;
int T;
int a[N];
int b[N],c[N];
int x[N];
map<int,bool> mp;
void solve()
{
    int n,m,l,q;
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    cin>>m;
    for(int i=1;i<=m;i++) cin>>b[i];
    cin>>l;
    for(int i=1;i<=l;i++) cin>>c[i];
    cin>>q;
    for(int i=1;i<=q;i++) cin>>x[i];
    	for(int i=1;i<=n;i++)
    	{
    		for(int j=1;j<=m;j++)
    		{
    			for(int k=1;k<=l;k++)
    			{
    				int s=a[i]+b[j]+c[k];
    				mp[s]=1;
    			}
    		}
    	}
    	int ans=0;
    	for(int i=1;i<=q;i++)
    	{
    		if(mp[x[i]]) cout<<"Yes"<<endl;
    		else cout<<"No"<<endl;
    	}

}
signed main()
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    T=1;
    //cin>>T;
    while(T--)
     {
         solve();
     }
     return 0;
} 

K - String Bags

题意:

有n个袋子,每个袋子里有若干个字符串。
给定一个目标串 t,要求从每个袋子选出最多 1个字符串,按顺序拼接成 t。
问取出来的字符串个数的最小值。

解题思路:

类似于01背包,每一个袋子可以取也可以不取。
d p [ i ] [ j ] dp[i][j] dp[i][j]表示从前 i i i个袋子里取出能拼成前 j j j个字符的最小次数
状态转移方程为: d p [ i ] [ j ] = m i n ( d p [ i ] [ j ] , d p [ i − 1 ] [ j − l e n ] + 1 ) dp[i][j]=min(dp[i][j],dp[i-1][j-len]+1) dp[i][j]=min(dp[i][j],dp[i1][jlen]+1)

代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e5+100,mod=998244353;
typedef long long ll;
typedef pair<pair<int,int>,int> PII;
int T;
string t;
int n;
string op[110][11];
int dp[110][110];
int c[N];
void solve()
{
   cin>>t;
   int ls=t.size();
   cin>>n;
   for(int i=1;i<=n;i++)
   {
   	cin>>c[i];
   	for(int j=1;j<=c[i];j++) cin>>op[i][j];
   }
   for(int i=0;i<=109;i++)
   	for(int j=0;j<=109;j++)
   		dp[i][j]=1e3;
   dp[0][0]=0;
   for(int i=1;i<=n;i++)
   {
   	dp[i][0]=0;
   	for(int len=0;len<=ls;len++)
   	{
   		dp[i][len]=dp[i-1][len];
   		for(int j=1;j<=c[i];j++)
   		{
   			int l=op[i][j].size();
   			if(len<l) continue;
   			string kk=t.substr(len-l,l);
   			if(op[i][j]==kk)
   			{
   				dp[i][len]=min(dp[i][len],dp[i-1][len-l]+1);
   			}
   		}
   	}
   }
   if(dp[n][ls]>=1e3) cout<<"-1"<<endl;
   else cout<<dp[n][ls]<<endl;
}
signed main()
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    T=1;
    //cin>>T;
    while(T--)
     {
         solve();
     }
     return 0;
} 

发现 d p dp dp数组第一维只由前一个状态更新过来的,所以可以用滚动数组优化:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e5+100,mod=998244353;
typedef long long ll;
typedef pair<pair<int,int>,int> PII;
int T;
string t;
int n;
string op[110][110];
int dp[1010];
int c[N];
void solve()
{
   cin>>t;
   int ls=t.size();
   cin>>n;
   for(int i=1;i<=n;i++)
   {
   	int s;
   	cin>>s;
   	c[i]=s;
   	for(int j=1;j<=s;j++) cin>>op[i][j];
   }
   	for(int j=0;j<=110;j++)
   		dp[j]=1e9;
   dp[0]=0;
   for(int i=1;i<=n;i++)
   {
   	for(int len=ls;len>=0;len--)
   	{
   		for(int j=1;j<=c[i];j++)
   		{
   			int l=op[i][j].size();
   			if(len<l) continue;
   			string kk=t.substr(len-l,l);
   			if(op[i][j]==kk)
   			{
   				dp[len]=min(dp[len],dp[len-l]+1);
   			}
   		}
   	}
   }
   if(dp[ls]==1e9) cout<<"-1"<<endl;
   else cout<<dp[ls]<<endl;
}
signed main()
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    T=1;
    //cin>>T;
    while(T--)
     {
         solve();
     }
     return 0;
} 

L - Insert or Erase

题意:

给定一个数组 a a a,进行以下 q q q次操作,分两种。

  • 1   x x x   y y y,在 x x x后面插入 y y y
  • 2   x x x,将 x x x删去。

保证每次操作后,各个数互不相同。
经过 q q q次操作后,输出最后的数组 a a a

解题思路:

模拟即可。用数组模拟双链表,记录每一个点的左边元素是哪个,右边元素是哪个,同时记录头结点。由于 x , y x,y x,y的范围都比较大,可以用 m a p map map存储。最后遍历链表即可。

代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e5+100,mod=998244353;
typedef long long ll;
typedef pair<pair<int,int>,int> PII;
int T;
int n;
int a[N];
map<int,int> l;
map<int,int> r;
int h=-1;
void solve()
{
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	h=a[1];
	for(int i=1;i<=n;i++)
	{
		if(i==1) l[a[1]]=-1,r[a[1]]=a[2];
		if(i==n) l[a[n]]=a[n-1],r[a[n]]=-1;
		else {
			l[a[i]]=a[i-1];
			r[a[i]]=a[i+1];
		}
	}
	int q;
	cin>>q;
	while(q--)
	{
		int id;
		cin>>id;
		if(id==1)
		{
			int x,y;
			cin>>x>>y;
			if(r[x]!=-1)
			{
				l[r[x]]=y;
				r[y]=r[x];
				r[x]=y;
				l[y]=x;
			}
			else
			{
				r[x]=y;
				l[y]=x;
				r[y]=-1;
			}

		}
		else 
		{
			int x;
			cin>>x;
			if(h==x)
			{
				h=r[x];
				l[r[x]]=-1;
			}
			else if(r[x]==-1)
			{
				r[l[x]]=-1;
			}
			else
			{
				r[l[x]]=r[x];
				l[r[x]]=l[x];
			}
		}
	}
    for(int i=h;i!=-1;i=r[i])
    {
    	cout<<i<<" ";
    }
   
}
signed main()
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    T=1;
    //cin>>T;
    while(T--)
     {
         solve();
     }
     return 0;
} 

M - Earn to Advance

太难了

题意:

有一个 N × N N \times N N×N 的网格,高桥最开始在位置 ( 1 , 1 ) (1,1) (1,1),有 0 0 0 个金币。

假设高桥现在处于位置 ( i , j ) (i,j) (i,j),每一秒他
可以做出如下选择中的一种:

  • 不移动,获得 P i , j P_{i,j} Pi,j 的金币。
  • 移动到 ( i , j + 1 ) (i,j+1) (i,j+1),花费 R i , j R_{i,j} Ri,j 的金币。
  • 移动到 ( i + 1 , j ) (i+1,j) (i+1,j),花费 D i , j D_{i,j} Di,j 的金币。

高桥在任意位置的金币数都不能小于 0 0 0

求高桥到达 ( N , N ) (N,N) (N,N) 的最小时间。

样例输入 #1

3
1 2 3
3 1 2
2 1 1
1 2
4 3
4 2
1 5 7
5 3 3

样例输出 #1

8

样例 #2

样例输入 #2

3
1 1 1
1 1 1
1 1 1
1000000000 1000000000
1000000000 1000000000
1000000000 1000000000
1000000000 1000000000 1000000000
1000000000 1000000000 1000000000

样例输出 #2

4000000004

解题思路:

在转移时考虑前一个点 ( k , l ) (k, l) (k,l),其中满足 p k , l ≤ p i , j p_{k, l} \leq p_{i, j} pk,lpi,j。两点间的最小移动代价可以事先通过 O ( 8 0 4 ) O(80^4) O(804) 预处理得到。然后在 ( k , l ) (k, l) (k,l) 充够钱后到达 ( i , j ) (i, j) (i,j)
因此,到达点 ( i , j ) (i, j) (i,j) 的方式有好多种,不同的方式有不同的最小步数和当前钱数。要保留哪个呢?哪个是最优的呢?可以观察到,我们保留步数最小的,如果步数相同,则保留钱数最多的,这样转移一定最优的。
通过 O ( 8 0 4 ) O(80^4) O(804) 的预处理,计算出所有点对之间的最小移动代价。

  • 对于每个点 ( i , j ) (i, j) (i,j),遍历所有满足 p k , l ≤ p i , j p_{k, l} \leq p_{i, j} pk,lpi,j 的点 ( k , l ) (k, l) (k,l)
  • 计算从 ( k , l ) (k, l) (k,l) ( i , j ) (i, j) (i,j) 的步数和钱数。
  • 保留步数最小的转移方式,如果步数相同,则保留钱数最多的转移方式。

通过这种方式,可以确保在每个点 ( i , j ) (i, j) (i,j) 处选择最优的转移方式,从而在整个路径上达到最优解。

符号解释
  • h i , j h_{i,j} hi,j:从 ( 1 , 1 ) (1,1) (1,1) 走到 ( i , j ) (i,j) (i,j) 所需的最小步数。
  • q i , j q_{i,j} qi,j:从 ( 1 , 1 ) (1,1) (1,1) 走到 ( i , j ) (i,j) (i,j) 步数的最小情况下剩余最多钱的数量。
  • g i , j , k , l g_{i,j,k,l} gi,j,k,l:从 ( k , l ) (k,l) (k,l) ( i , j ) (i,j) (i,j) 需要最少钱的数量(需要保证 k ≤ i , l ≤ j k \leq i, l \leq j ki,lj)。
  • P k , l P_{k,l} Pk,l:在点 ( k , l ) (k,l) (k,l) 的某种成本或费用。
实现思路
  1. 初始化 h h h q q q 数组。
  2. 使用动态规划的方法,从 ( 1 , 1 ) (1,1) (1,1) 开始逐步计算每个点的 h h h q q q 值。
  3. 在计算过程中,使用上述转移方程来更新 h h h 值。
  4. 最终得到 h n , n h_{n,n} hn,n 即为所求。
转移方程解释

h i , j = min ⁡ k ≤ i , l ≤ j ( h k , l + ⌈ max ⁡ ( g i , j , k , l − q k , l , 0 ) P k , l ⌉ + ( i − k ) + ( j − l ) ) h_{i,j} = \min_{k \leq i, l \leq j} \left( h_{k,l} + \left\lceil \frac{\max(g_{i,j,k,l} - q_{k,l}, 0)}{P_{k,l}} \right\rceil + (i - k) + (j - l) \right) hi,j=minki,lj(hk,l+Pk,lmax(gi,j,k,lqk,l,0)+(ik)+(jl))

这个方程的含义是从 ( 1 , 1 ) (1,1) (1,1) ( i , j ) (i,j) (i,j) 的最小步数 h i , j h_{i,j} hi,j 可以通过以下方式计算:

  1. 遍历所有可能的前一个点 ( k , l ) (k,l) (k,l),其中 k ≤ i k \leq i ki l ≤ j l \leq j lj
  2. 对于每个前一个点 ( k , l ) (k,l) (k,l),计算从 ( k , l ) (k,l) (k,l) ( i , j ) (i,j) (i,j) 的额外步数 ( i − k ) + ( j − l ) (i - k) + (j - l) (ik)+(jl)
  3. 计算从 ( k , l ) (k,l) (k,l) ( i , j ) (i,j) (i,j) 所需的额外费用,并将其转换为步数。这个额外费用是 g i , j , k , l − q k , l g_{i,j,k,l} - q_{k,l} gi,j,k,lqk,l,如果这个值为负,则取 0。
  4. 将这个额外费用除以 P k , l P_{k,l} Pk,l 并向上取整,得到需要的额外步数。
  5. 将所有这些值相加,并取最小值作为 h i , j h_{i,j} hi,j
最终答案

最终的答案是从 ( 1 , 1 ) (1,1) (1,1) ( n , n ) (n,n) (n,n) 的最小步数,即 h n , n h_{n,n} hn,n

代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e5+100,mod=998244353;
typedef long long ll;
typedef pair<pair<int,int>,int> PII;
int T;
int n;
const int M=82;
int p[M][M];
int r[M][M];
int d[M][M];
int dist[M][M][M][M];
int ans[M][M];
int q[M][M];
void solve()
{
	cin>>n;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			cin>>p[i][j];
	for(int i=1;i<=n;i++)
		for(int j=1;j<n;j++)
			cin>>r[i][j];
	for(int i=1;i<n;i++)
		for(int j=1;j<=n;j++)
			cin>>d[i][j];
	memset(dist,0x3f,sizeof dist);
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			dist[i][j][i][j]=0;
			for(int k=i;k>=1;k--)
			{
				for(int l=j;l>=1;l--)
				{
					if(k==i&&l==j) continue;
					dist[i][j][k][l]=min(dist[i][j][k+1][l]+d[k][l],dist[i][j][k][l+1]+r[k][l]);
				}
			}
		}
	}
	memset(ans,0x3f,sizeof ans);
	ans[1][1]=0;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			for(int k=1;k<=i;k++)
			{
				for(int l=1;l<=j;l++)
				{
					int t=max((dist[i][j][k][l]-q[k][l]+p[k][l]-1)/p[k][l],0ll);
					if(ans[k][l]+t+i-k+j-l<ans[i][j])
					{
						ans[i][j]=ans[k][l]+t+i-k+j-l;
						q[i][j]=q[k][l]+t*p[k][l]-dist[i][j][k][l];
					}
					else if(ans[k][l]+t+i-k+j-l==ans[i][j]&&q[i][j]<q[k][l]+t*p[k][l]-dist[i][j][k][l])
					{
						q[i][j]=q[k][l]+t*p[k][l]-dist[i][j][k][l];
					}

				}
			}
		}
	}
	cout<<ans[n][n]<<endl;



}
signed main()
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    T=1;
    //cin>>T;
    while(T--)
     {
         solve();
     }
     return 0;
} 

N - Points and Comparison

不会

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值