• 暑期集训第九天(6-30)题解及总结


    小概括:

    今天的考试中老师可能对我们过于高估了...四道之中出了两道紫题,于是这次考试之中的分数基本都很低(除了AK的gyz大佬),DZN今天终于超过了lc(排除提示的问题)今天晚上周围集体都在打树剖,可能认为老师明天要考???

    T1:浇水

     听说这道题要用贪心?线段树?对不起,最短路解决一切问题,我们考虑题目中要求覆盖整个花园,其实可以转化为覆盖上边界(下边界同理),我们就可以处理出每个喷子的可以喷到的上边界的范围,之后就是把他们拼起来即可,之后就和昨天的最后一道题一样了,注意喷子够不到上边界的要判掉,不然就算成负权的加入图中了

    (链接跳转:昨日博客:https://www.cnblogs.com/li-jia-hao/p/13210357.html

    老姚贪心写法https://www.cnblogs.com/hbhszxyb/p/13212743.html;)

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e6+10;
    struct Node{
    	int next,to,dis;
    }edge[N];
    int Head[N],tot;
    void Add(int x,int y,int z){
    	edge[++tot].to=y;
    	edge[tot].dis=z;
    	edge[tot].next=Head[x];
    	Head[x]=tot;
    }
    struct Edge{
    	int id,dis;
    	Edge(int x,int y){
    		id=x;dis=y;
    	}
    	bool operator < (const Edge &a)const{
    		return a.dis<dis;
    	}
    };
    priority_queue<Edge>q;
    int dis[N],vis[N];
    void dijkstra(int x){
    	memset(dis,0x3f,sizeof(dis));
    	memset(vis,0,sizeof(vis));
    	dis[x]=0;q.push(Edge(x,0));
    	while(!q.empty()){
    		int u=q.top().id;q.pop();
    		if(vis[u]) continue;
    		vis[u]=1;
    		for(int i=Head[u];i;i=edge[i].next){
    			int v=edge[i].to;
    			if(dis[v]>dis[u]+edge[i].dis){
    				dis[v]=dis[u]+edge[i].dis;
    				q.push(Edge(v,dis[v]));
    			}
    		}
    	}
    }
    int main(){
    	//freopen("a.in","r",stdin);
    	//freopen("a.out","w",stdout);
    	int k,n,m;
    	scanf("%d%d%d", &k, &n, &m);
    	for(int i = 1; i <= k; ++ i){
    		int x,y,l,r;
    		scanf("%d%d",&x,&y);
    		x++;
    		if(y < m/2) continue;
    		l=x-(int)sqrt(y*y-m*m/4);
    		r=x+(int)sqrt(y*y-m*m/4);
    		//printf("%d %d %d
    ",i,l,r);
    		l=max(l,1);
    		r=min(r,n+1);
    		Add(l,r+1,1);
    	}
    	for(int i=1;i<=n+2;++i)
    		Add(i+1,i,0);
    	dijkstra(1);
    	if(dis[n]==0x3f3f3f3f) printf("-1
    ");
    	else printf("%d
    ",dis[n+1]);
    	return 0;
    }
    

     T2:免费馅饼

     这道题后来被削弱了,老师看我们对字典序不怎么理解,就把第二部分的路径输入给去了(然鹅我自信满满的dp只得了30pts),如果不考虑路径的话这道题还是挺简单的,就是一个线性dp,我们预先处理出i时在j可以得到的分数,dp[i][j]表示时间为i时在j的答案,于是我们从答案可能转移来的方向进行转移求最大值即可,我30pts是因为忘了原地踏步的情况,记得初始化及特判高度为1的情况,

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e6+10;
    int score[1500][150];
    int dp[1500][150]; //i秒的时候在j时的答案;
    int n,m,t_max,sum;
    int main(){
    	//freopen("a.in","r",stdin);
    	//freopen("a.out","w",stdout);
    	scanf("%d%d",&n,&m);
    	int a,b,c,d;
    	while(scanf("%d%d%d%d",&a,&b,&c,&d)==4){
    		if(m==1){
    			score[a][b]+=d;sum+=d;
    			t_max=max(a,t_max);
    			continue;
    		}
    		if((m-1)%c!=0) continue;
    		int t=((m-1)/c); 
    		score[a+t][b]+=d;
    		t_max=max(a+t,t_max);
    	}
    	memset(dp,128,sizeof(dp));
    	dp[0][(n+1)/2]=0;
    	for(int i=1;i<=t_max;++i)
    		for(int j=1;j<=n;++j){
    			if(dp[i][j]<dp[i-1][j]+score[i][j]){
    				dp[i][j]=dp[i-1][j]+score[i][j];
    			}
    			if(j>1)
    				if(dp[i][j]<dp[i-1][j-1]+score[i][j]){
    					dp[i][j]=dp[i-1][j-1]+score[i][j];
    				}
    			if(j<=n-2)
    				if(dp[i][j]<dp[i-1][j+2]+score[i][j]){
    					dp[i][j]=dp[i-1][j+2]+score[i][j];
    				}
    			if(j<=n-1)
    				if(dp[i][j]<dp[i-1][j+1]+score[i][j]){
    					dp[i][j]=dp[i-1][j+1]+score[i][j];
    				}
    			if(j>2)
    				if(dp[i][j]<dp[i-1][j-2]+score[i][j]){
    					dp[i][j]=dp[i-1][j-2]+score[i][j];
    				}
    				
    		}
    	int ans=0;
    	for(int i=1;i<=n;++i){
    		ans=max(ans,dp[t_max][i]);
    	}
    	if(n==1&&m==1){
    		printf("%d
    ",sum);
    		return 0;
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    

     T3:压缩

     这是考试之中我唯一一道A掉的dp题,我们用dp[i][j][k]表示从i到j的情况,k=1表示有M,否则表示没有M,那么我们存在以下几个转移:

    1. dp[i][j][0]=min(dp[i][j][0],dp[i][mid][0]+1)(当且仅当字符串对称)

    2. dp[i][j][0]=min(dp[i][j][0],dp[i][k][0]+j-k)

    3. dp[i][j][1]=min(dp[i][j][1],min(dp[i][k][1],dp[i][k][0])+min(dp[k+1][j][1],dp[k+1][j][0])+1)(最后加的一是缺失的M);

    之后我们闭着眼进行转移就行了.

    #include<bits/stdc++.h>
    #define debug printf("-debug-
    ")
    using namespace std;
    const int N=1e6+10;
    char a[N];
    int dp[250][250][2];
    bool Judge(int l,int r){
    	if((r-l+1)%2!=0) return 0;
    	int mid=l+(r-l+1)/2;
    	while(mid<=r){
    		if(a[l]!=a[mid]) return 0;
    		l++;mid++;
    	}
    	//debug;
    	return 1;
    }
    int main(){
    	//freopen("a.in","r",stdin);
    	//freopen("a.out","w",stdout);
    	scanf(" %s",a+1);
    	int len=strlen(a+1);
    	for(int i=1;i<=len;++i)
    		for(int j=i;j<=len;++j)
    			dp[i][j][0]=dp[i][j][1]=j-i+1;
    	//debug;
    	for(int d=2;d<=len;++d)
    		for(int i=1,j;(j=i+d-1)<=len;++i){
    			//debug;
    			int mid=i+(j-i+1)/2-1;
    			if(Judge(i,j)) dp[i][j][0]=min(dp[i][j][0],dp[i][mid][0]+1);
    			for(int k=i;k<=j;++k)
    				dp[i][j][0]=min(dp[i][j][0],dp[i][k][0]+j-k);
    			for(int k=i;k<j;++k)
    				dp[i][j][1]=min(dp[i][j][1],min(dp[i][k][1],dp[i][k][0])+min(dp[k+1][j][1],dp[k+1][j][0])+1);
    		}
    	//debug;
    	printf("%d
    ",min(dp[1][len][1],dp[1][len][0]));
    	return 0;
    }
    

     T4:枪战Maf

     我们老师介绍这道题属于那种看起来就有思路,越看越有思路,但是越写越绝望的题目,因为考场上没时间细想,不怎么以为然,知道我开始详细的打这道题....

    这道题一看就能想到tarjan缩点,但是如果这样的话你就掉进出题人的陷阱了,这道题的思路我感觉...自己想出来的非神即犇.

    考虑如果单独的一个环,最大的情况就是一个人打完后立马被另一个人打死,这样死的人数就是环的大小减一,最大就是环的大小除以2

    如果是链那?最多人数当然是除了出度为0的点的人以外全部死掉,最少人数就是大小处以二.我们可以对每个人进行处理,入度为0的点必不死,可死可不死的人可以打上标记,稍后处理,之后对环单独处理,这里的思路可能有些不好想,推一下老师的博客:https://www.cnblogs.com/hbhszxyb/p/13212782.html

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e6+10;
    int die[N],undie[N];
    int q[N],Max,Min,ru[N],to[N];
    int main(){
    	int n;
    	scanf("%d",&n);
    	for(int i=1;i<=n;++i){
    		scanf("%d",&to[i]);
    		ru[to[i]]++;
    	}
    	for(int i=1;i<=n;++i)
    		if(ru[i]==0){
    			Min++;
    			q[++Max]=i;
    		}
    	int head=0;
    	while(head<=Max){
    		int top=q[head];head++;
    		if(die[to[top]]) continue;
    		die[to[top]]=1;
    		int live=to[to[top]];
    		ru[live]--;
    		undie[live]=1;
    		if(ru[live]==0) q[++Max]=live;
    	}
    	for(int i=1;i<=n;++i)
            if(ru[i]&&!die[i]){
                int len=0,flag=0;
                for(int j=i;!die[j];j=to[j]){
                    len++;
                    flag|=undie[j];
                    die[j]=1;
                }
                if(!flag && len>1) Min++;
                Max+=len/2;
        } 
        printf("%d %d",n-Max,n-Min);
        return 0;
    }
    

     总结:

    今天的考试我感觉还是挺难的,虽然出现了集训以来的第一个AK(G大佬),但是对于我们这些小萌新来说就是虐杀呀......平时只练蓝题还是不够,平时还是要穿插一些有难度的题目呀,高三学长们马上要高考了,在现在的机房也待不了几天了,希望明天可以取得一个好成绩吧,.

  • 相关阅读:
    [LeetCode]题解(python):119-Pascal's Triangle II
    [LeetCode]题解(python):118-Pascal's Triangle
    [LeetCode]题解(python):117-Populating Next Right Pointers in Each Node II
    寒假自学进度8
    寒假自学进度7
    寒假自学进度6
    寒假自学5
    寒假自学学习4
    寒假自学进度3
    寒假自学进度2
  • 原文地址:https://www.cnblogs.com/li-jia-hao/p/13216204.html
Copyright © 2020-2023  润新知