• CF习题集三


    CF习题集三

    一、CF8C Looking for Order

    题目描述

    (Lena)喜欢秩序井然的生活。一天,她要去上大学了。突然,她发现整个房间乱糟糟的——她的手提包里的物品都散落在了地上。她想把所有的物品都放回她的手提包。但是,这里有一点问题:她一次最多只能拿两个物品,她也不能移动她的手提包。并且,因为她爱整洁的习惯,如果她拿起了一个物品,她也不能将它放在其他地方,除非放回她的手提包。

    (Lena)把她的房间划分为了一个平面直角坐标系。现在Lena给你她的手提包和每个散落的物品的坐标(当然,一开始的时候她就和手提包站在一个地方)。她从坐标 ((x1,y1))走到坐标 ((x2,y2)) 需要用 ((x1-x2)^2+(y1-y2)^2) 单位的时间。现在,(Lena)将告诉你她的房间的情况,请你为(Lena)找到一个拾起每个物品的顺序,使她拾起所有物品所需的总时间最小。当然,(Lena)最后需要返回她的手提包。

    分析

    (n)的范围比较小,因此我们考虑状压(DP)

    我们设(f[i])为当前拾取的物品状态为(i)时的最小花费

    那么我们就可以写出如下的状态转移方程

    f[i|(1<<(j-1))|(1<<(k-1))]=f[i]+dis[j][0]+dis[j][k]+dis[k][0];
    

    如果单纯地这样写而不加任何优化是会(T)掉的

    实际上,对于两个物品,我们先拿走哪一个或者后拿走哪一个对结果没有影响

    因此,我们可以人为地规定一个拿取的顺序

    即对于一个(j)一旦它匹配成功,我们就跳出循环,不再匹配

    而去匹配下一个(j)

    代码

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=25;
    int f[1<<maxn],jl[1<<maxn];
    int jlx[maxn],jly[maxn],dis[maxn][maxn];
    int ans[1<<maxn],cnt;
    int main(){
        memset(f,0x3f,sizeof(f));
        scanf("%d%d",&jlx[0],&jly[0]);
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d%d",&jlx[i],&jly[i]);
        }
        for(int i=0;i<=n;i++){
            for(int j=i+1;j<=n;j++){
                dis[i][j]=dis[j][i]=(jlx[i]-jlx[j])*(jlx[i]-jlx[j])+(jly[i]-jly[j])*(jly[i]-jly[j]);
            }
        }
        f[0]=0;
        int mmax=(1<<n)-1;
        for(int i=0;i<=mmax;i++){
            for(int j=1;j<=n;j++){
                if(i&(1<<(j-1))) continue;
                for(int k=1;k<=n;k++){
                    if(i&(1<<(k-1))) continue;
                    int now=i|(1<<(j-1))|(1<<(k-1));
                    if(f[now]<=f[i]+dis[j][0]+dis[j][k]+dis[k][0]) continue;
                    f[now]=f[i]+dis[j][0]+dis[j][k]+dis[k][0];
                    jl[now]=i;
                }
                break;
            }
        }
        printf("%d
    ",f[mmax]);
        while(mmax){
            printf("0 ");
            for(int i=1;i<=n;i++){
                if((mmax^jl[mmax])&(1<<((i-1)))) printf("%d ",i);
            }
            mmax=jl[mmax];
        }
        printf("0 ");
        return 0;
    }
    

    二、CF510D Fox And Jumping

    题目描述

    给出 (n) 张卡片,分别有 (l_i)​ 和 (c_i)​。在一条无限长的纸带上,你可以选择花 (c_i)​ 的钱来购买卡片 (i),从此以后可以向左或向右跳 (l_i)​ 个单位。问你至少花多少元钱才能够跳到纸带上全部位置。若不行,输出 (-1)

    分析

    我们先只考虑购买了两个卡片的情况

    我们设这两个卡片跳跃的距离分别为(a,b)

    其中第一张卡片使用了(x)次,第二张卡片使用了(y)

    那么跳跃的距离(l=ax+by)

    要使方程有解,则必有(l mod gcd(a,b)=0)

    要使(l)取到任意整数,则(gcd(a,b)=1)

    因此,原题就变成了在(n)个数中选取几个数,使得这些数的最大公因数为(1)

    求所有方案中花费最小的方案

    一种可行的做法是设(f[i])表示选择一些数并且最大公约数为(i)时的最小花费

    利用(map)进行转移

    但是现在我们考虑怎么用暴搜过掉这个题

    最初始的暴搜应该比较好定义

    我们传四个参数,分别是当前已经选到了第几个数、当前已经选了几个数、当前选择的所有数的价值之和、当前所有数的最大公因数

    这样的暴搜不加任何剪枝会(T)到飞起

    因此我们考虑怎么去优化

    剪枝一、如果当前价值已经大于所选价值,那么就没有必要继续向下选

    剪枝二、如果当前选择的数量大于9,那么就没有必要继续向下选

    因为如果有(10)个不同的质因子,此时数会很大,已经超过了(l[i])的最大值

    剪枝三、提前将-1的情况预处理出来

    剪枝四、如果当前枚举的(l[i])是之前枚举的公因数的倍数,那么当前的(l[i])一定不会使之后的公因数变小,选(l[i])无意义

    有了上述四个剪枝,我们已经可以跑过(64)个点,但是在最后一个点(T)

    因此,我们再加最后一个信仰剪枝,即如果当前运行次数过多,直接输出当前最优解,同时杀死程序

    代码

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=1005,maxk=21;
    int s[maxn],l[maxn],n;
    int gcd(int aa,int bb){
    	if(bb==0) return aa;
    	return gcd(bb,aa%bb);
    }
    int ans=0x3f3f3f3f;
    int tim=0;
    void dfs(int now,int cnt,int tot,int gys){
    	tim++;
    	if(tim>2e7){
    		if(ans==0x3f3f3f3f) printf("-1
    ");
    		else printf("%d
    ",ans);
    		exit(0);
    	}
    	if(gys==1) ans=min(ans,tot);
    	if(tot>=ans || cnt>9 || now>n) return;
    	for(int i=now;i<=n;i++){
    		if(gys==0) dfs(i+1,cnt+1,tot+s[i],l[i]);
    		else if(l[i]%gys!=0) dfs(i+1,cnt+1,tot+s[i],gcd(gys,l[i]));
    	}
    }
    int main(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++){
    		scanf("%d",&l[i]);
    	}
    	for(int i=1;i<=n;i++){
    		scanf("%d",&s[i]);
    	}
    	int jud=l[1];
    	for(int i=2;i<=n;i++){
    		jud=gcd(jud,l[i]);
    	}
    	if(jud!=1){
    		printf("-1
    ");
    		return 0;
    	}
    	dfs(1,0,0,0);
    	if(ans!=0x3f3f3f3f)printf("%d
    ",ans);
    	else printf("-1
    ");
    	return 0;
    }
    
    

    三、CF985E Pencils and Boxes

    题目描述

    给出(n)个整数(a_1,a_2,...,a_n),现在需要对其进行分组,使其满足以下条件:
    每个数都必须恰好分入一组中

    每一组中必须至少包含(k)个数

    在每一组中,整数的权值之差的绝对值不能超过(d)。即当(a_i,a_j)​在同一组时,需要满足(|a_i+a_j| leq d)

    请判断是否存在满足条件的分组方案,若有请输出"YES",否则输出"NO"。

    数据范围:(1 leq k leq n leq 5 * 10^5 , 0 leq d leq 10^9)

    分析

    这道题我们要用到(bool)类型的(DP)

    为了方便处理,我们将所有数从小到大排一下序

    我们设(f[i])为当前遍历到第(i)个数,是否合法

    其中值为(1)代表合法,值为(0)代表不合法

    在转移时需要枚举每一个左端点,如果左端点的状态合法,我们再用当前的状态不断向右更新

    最后如果(f[n])(1),那么输出YES,否则输出NO

    代码

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=1e6+5;
    typedef long long ll;
    bool f[maxn];
    ll a[maxn];
    int main(){
        ll n,k,d;
        scanf("%lld%lld%lld",&n,&k,&d);
        for(ll i=1;i<=n;i++){
            scanf("%lld",&a[i]);
        }
        sort(a+1,a+1+n);
        f[0]=1;
        ll p=1;
        for(ll i=0;i<=n;i++){
        	if(f[i]){
        		p=max(p,i+k);
        		while(p<=n && a[p]-a[i+1]<=d){
        			f[p]=1;
        			p++;
        		}
        	}
        }
        if(f[n]==0) printf("NO
    ");
        else printf("YES
    ");
        return 0;
    }
    

    四、CF1340B Nastya and Scoreboard

    题目描述

    传送门

    分析

    比较有意思的一道题
    直接放个链接题解

    代码

    #include<bits/stdc++.h>
    using namespace std;
    const int N=2e3+10;
    char num[10][10]={"1110111","0010010","1011101","1011011","0111010","1101011","1101111","1010010","11111111","1111011"};
    int dp[N][N];
    char a[N][10];
    int cal(char c[],int pos){
    	int cnt=0;
    	for(int i=0;i<7;++i){
    		if(c[i]=='1' && num[pos][i]=='0') return -1;
    		if(c[i]!=num[pos][i]) cnt++;
    	}
    	return cnt;
    }
    int main(){
        memset(dp,-1,sizeof(dp));
    	int n,k;
    	scanf("%d%d",&n,&k);
    	for(int i=1;i<=n;++i)
    		scanf(" %s",a[i]);
    	for(int i=n;i>=1;--i){
    		if(i==n){
    			for(int j=0;j<10;++j){
    				int t=cal(a[i],j);
    				if(t==-1) continue;
    				dp[n][t]=max(dp[n][t],j);
    			}
    		}
    		else{
    			for(int j=0;j<10;++j){
    				int t=cal(a[i],j);
    				if(t==-1) continue;
    				for(int p=t;p<=k;++p){
    					if(dp[i+1][p-t]!=-1){
    						dp[i][p]=j;
    					}
    				}
    			}
    		}
    	}
    	if(dp[1][k]==-1){
    		puts("-1");
    		return 0;
    	}
    	int now=k;
    	for(int i=1;i<=n;++i){
    		printf("%d",dp[i][now]);
    		now-=cal(a[i],dp[i][now]);
    	}
    	puts("");
    	return 0;
    }
    
    
  • 相关阅读:
    如何快速打开Github
    vuecli4 如何创建带有vuerouter vuex scss预编译的项目
    ASP.Net Core WebApi几种版本控制对比
    解决Asp.Net Core 3.1 中无法读取HttpContext.Request.Body的问题
    winrar压缩文件但是排除指定目录
    postgresql数据库下导入导出,删除常用命令
    .NetCore使用Swagger+API多版本控制
    ElementUI和Ant Design对比
    自动登录或7天登录的实现
    浏览器脚本按钮功能
  • 原文地址:https://www.cnblogs.com/liuchanglc/p/13431102.html
Copyright © 2020-2023  润新知