2022.10.4测试

加班熬夜改出来的。

考的不好,只有 \(100pts\),继续努力。

T1:预计黄(1200)

T2:预计蓝(2200)

T3:预计紫(2600)

T1:

1.有两个年轻人
题目背景
有人问我,发生甚么事了?
我一看,哦!原来是昨天,有两个年轻人,一个数学考150,一个物理考110,在教室里练题。
我走上前去,啪,就扔出来了一道题目,很快啊!
他们说,我这个题不好做;我说我这个好做。
他们不信!我就叫你给他们说一说,该怎么做。
题目描述
桌子上刚开始有 堆棋子,第 堆棋子有 个棋子。
两个年轻人轮流操作。每次操作,可以从当前剩余的所有棋子堆中,选择出棋子数量最少的(如果有多
堆棋子满足条件,则在它们中任选一堆)某一堆棋子,然后从中拿走任意数量的棋子。
要求拿走的数量不能为 0 ,不能超过这一堆所剩余的棋子数。
拿走桌子上最后一颗棋子的人获胜。
请问,在当前局面下,两个年轻人都采用最优策略,先手的人是否能够取胜?
输入格式
第一行,一个正整数 ,表示测试组数。
每组数据,第一行,一个整数 。
接下来一行, 个整数, 表示第 堆的棋子数。
输出格式
输出 行。
对每组数据,如果先手存在必胜策略,则输出 Yes
否则输出 No
样例 #1
样例输入 #1
2
2
1 1
1
3
样例输出 #1
提示
【样例1解释】
第一局,先手拿走第一堆的棋子,只能拿一颗。后手拿走第二堆的棋子,后手获得胜利。
第二句,先手拿走第一堆的全部棋子,先手获得胜利。
【数据范围】:
对30%的数据满足,
对60%的数据满足,
对100%的数据满足,0<T<=10,n<=1e4,ai<=1e18
时间限制 1.00s
内存限制 256.00MB


推一下即可知道,如果在最小的那堆大于 \(1\) 的情况下,先手可以将其强制变成 \(1\),后手只能选那个 \(1\),剩下最后一堆先手直接选即可。

在初始有 \(1\) 的情况下,每有一个 \(1\),交换一次出手权。

特判全为 \(1\) 即可。

#include<iostream>
#include<cstdio>
#define int long long
using namespace std;
const int N=1e4+5;
int n;
signed main()
{
	int T;
	scanf("%lld",&T);
	while(T--)
	{
		int n;
		scanf("%lld",&n);
		bool flag=1,ss=0;
		for(int i=1;i<=n;i++)
		{
			int x;
			scanf("%lld",&x);
			flag^=(x==1);
			ss|=(x!=1);
		}
		if((flag&&ss)||(!flag&&!ss))printf("Yes\n");
		else printf("No\n");
	}
	return 0;
}

T2:

2.搬砖
题目背景
因为题目简单,所以 不好好学习,最后到了工地搬砖。
题目描述
来到工地,俯视来看,工地是一个二维平面,现在四周散落了 块砖。第 个砖在坐标
上。
现在需要把 块砖全部搬运到目的地 处。
决定开始搬砖,但是他同时最多只能携带两块砖,而且一旦他拿起一块砖,就必须要把砖放到目
的地处,不允许放在中途的某个地方。
搬砖的时候,对于每一次行动,从 按照直线走到 要花费
的时间。 拿起砖和放下砖不消耗时间。
注意,每次行动 只能走一条直线,且每次行动的终止点只能是当前某块砖的所在地或者目的地。
初始时刻, 在 处。
希望你找到一个搬砖顺序,使得 把所有砖搬到目的地花费的时间最少。
输入格式
第一行,两个整数 。
接下来一行,一个整数 ,表示砖的数量。
接下来 行,每行两个整数 。
No
Yes
输出格式
第一行,一个整数,表示把所有砖搬到目的地花费的最少时间。
第二行, 个整数,表示最优方案下,拾起每块砖的前后顺序。如果有多个答案,输出字典序最小的
解。
样例 #1
样例输入 #1
样例输出 #1
样例 #2
样例输入 #2
样例输出 #2
提示
【数据范围】:
对20%的数据满足,
对50%的数据满足,
对100%的数据满足,0<n<=100,|xi|,|yi|<=100。
保证给出的坐标互不相同。
时间限制 1.00s
内存限制 256.00MB


经典 TSP 问题,打炸了不应该。

对于每一个砖头是否已搬过进行状压,每次枚举搬的一块砖,或是两块砖,求最优解即可。

利用深搜实现这个过程,加上记忆化可通过。

至于答案顺序处理,从小到大枚举,中途记录下顺序就行了。

#include<iostream>
#include<cstdio>
#include<cstring>
#pragma GCC optimize(2)
using namespace std;
const int N=25;
struct node
{
    int x,y;
}a[N];
int xx,yy,n,t[N],ans=2e9,f[1<<(N-5)],vis[N];
void dfs(int num,int sum,int step)
{
    if(sum>=ans)return;
    if(f[num]<=sum)return;
    f[num]=sum; 
    if(step==n)
    {
        if(ans>sum)
        {
            ans=sum;
            for(int i=0;i<n;i++)t[i]=vis[i];
        }
        return;
    }
    for(int i=0;i<n;i++)
    {
        if(num&(1<<i))continue;
        for(int j=i;j<n;j++)
        {
            if(num&(1<<j))continue;
            if(i==j)
            {
                int lim=2*((xx-a[i+1].x)*(xx-a[i+1].x)+(yy-a[i+1].y)*(yy-a[i+1].y));
                vis[step]=i+1;
                dfs(num|(1<<i),sum+lim,step+1);
                continue;
            }
            int lim=(xx-a[i+1].x)*(xx-a[i+1].x)+(yy-a[i+1].y)*(yy-a[i+1].y);
            lim+=(a[j+1].x-a[i+1].x)*(a[j+1].x-a[i+1].x)+(a[j+1].y-a[i+1].y)*(a[j+1].y-a[i+1].y);
            lim+=(xx-a[j+1].x)*(xx-a[j+1].x)+(yy-a[j+1].y)*(yy-a[j+1].y);
            vis[step]=i+1;
            vis[step+1]=j+1;
            dfs(num|(1<<i)|(1<<j),sum+lim,step+2);
        }
    }
}
int main()
{
    memset(f,0x3f,sizeof(f));
    scanf("%d%d%d",&xx,&yy,&n);
    for(int i=1;i<=n;i++)scanf("%d%d",&a[i].x,&a[i].y);
    dfs(0,0,0);
    printf("%d\n",ans);
    for(int i=0;i<n;i++)printf("%d ",t[i]);
    return 0;
}

T3:

3.闪电链
题目背景
用好三维立体混元劲儿,
才打出松活弹抖闪电链。
题目描述
有一个长度为 的序列 。并且给出了一个整数 。
闪电链 是序列 的一个下标序列:
并且闪电链 必须满足以下要求:
(1) ,也就是说, 的首尾必须分别是 .
(2)对于任意的 , 的值满足以下条件之一: 或者
.
(3)另外,还需要满足 或者 .
有多少个不同的闪电链序列 满足以上条件?答案对 998244353 取模。
由于下标序列本身有序,两个下标序列不同,当且仅当他们的含有的数字不同。
输入格式
第一行,正整数 。
接下来一行, 个正整数 。
输出格式
输出闪电链的个数。答案对 998244353 取模。
样例 #1
样例输入 #1
样例输出 #1
提示
【样例解释】
{1,2,5}
5 1
2 3 2 4 3
4
{1,3,5}
{1,2,3,4,5}
{1,2,3,5}
【数据范围】
对于15%的数据,满足 .
对于30%的数据,满足 .
另有20%的数据,满足 .
另有20%的数据,满足 .
对于100%的数据,满足 2<=n<=1e5,1<=ai,h<=h-1。
时间限制 2.00s
内存限制 512.00M

由于求方案数,可以想到 dp。

\(f_{i,j}\) 代表以 \(i\) 结尾时上一步差为 \(j\) 的方案数。

可以列出暴力方程。

\[f_{i+j,j}=f_{i+j,j}+f_{i,j} \]

\[sum_i=\sum_{j=1}^{n}f_{i,j}-f_{i,a_i} \]

\[f_{i+a_i,a_i}=f_{i+a_i,a_i}+sum_i \]

复杂度:\(O(n^2)\)

考虑优化。

可以发现,在转移中途,其实有许多状态是空的,但它仍然被转移了。

那么我们可以用 map (或平衡树)打 dp 来解决这个问题。

但是会 T。

所以说对于一个数 \(k\),在 \(k\) 以下的正常处理,在 \(k\) 以上的由于数比较少,所以考虑用 map 来处理,这样就可以完美处理掉无用空间过多的问题。

时间复杂度:\(O(n\times\sqrt n\times\log(n))\)

#include<iostream>
#include<cstdio>
#include<vector>
#include<map>
using namespace std;
const int N=1e5+5;
const int k=800;
const int mod=998244353;
int n,h,f[N][k],a[N],sum[N];
map<int,int>dp[N];
signed main()
{
    scanf("%d%d",&n,&h);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    if(h<k)f[1][h]=1;
    else dp[1][h]=1;
    if(a[1]!=h)sum[1]=1;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<k;j++)
        {
            if(i+j>n)break;
            f[i+j][j]+=f[i][j],f[i+j][j]%=mod;
            if(j!=a[i+j])sum[i+j]+=f[i][j],sum[i+j]%=mod;
        }
        for(auto j:dp[i])
        {
            int x=j.first,y=j.second;
            if(i+x>n)break;
            dp[i+x][x]+=y,dp[i+x][x]%=mod;
            if(x!=a[i+x])sum[i+x]+=y,sum[i+x]%=mod;
        }
        if(i+a[i]>n)continue;
        if(a[i]<k)f[i+a[i]][a[i]]+=sum[i],f[i+a[i]][a[i]]%=mod;
        else dp[i+a[i]][a[i]]+=sum[i],dp[i+a[i]][a[i]]%=mod;
        if(a[i]!=a[i+a[i]])sum[i+a[i]]+=sum[i],sum[i+a[i]]%=mod;
    }
    int ans=0;
    for(int i=1;i<k;i++)ans+=f[n][i],ans%=mod;
    for(auto i:dp[n])ans+=i.second,ans%=mod;
    printf("%d",ans);
    return 0;
}
/*
5
1
2 2 2 2 2
*/
/*
5
1
2 2 2 2 2
*/
posted @ 2023-02-24 13:55  Gmt丶Fu9ture  阅读(36)  评论(0)    收藏  举报