zoukankan      html  css  js  c++  java
  • [第九场]题解

    T1

    题目描述
    给定一个n*m的矩阵,每次你可以选择前进一格或转弯(90度),求在不出这个矩阵的情况下遍历全部格点所需最少转弯次数。有多组数据
    输入
    第一行一个整数k,表示数据组数
    以下k行,每行两个整数n,m,表示矩阵大小
    输出
    输出一个整数,即最少转弯次数
    样例
    样例输入1
    2
    1 10
    10 1
    样例输出1
    0
    0
    样例输入2
    3
    1 1
    3 3
    3 4
    样例输出2
    0
    4
    4
    样例输入3
    2
    5 8
    6 4
    样例输出3
    8
    6

    解题思路

    很明显,一直走s型或蛇型即可,答案即(min(n,m)1)2(min(n,m) - 1)* 2

    代码

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<queue>
    #include<cstdlib>
    #include<vector>
    using namespace std;
    int k,n,m;
    int main(){
    	//freopen("kosnja.in","r",stdin);
    	//freopen("kosnja.out","w",stdout);
    	scanf ("%d",&k);
    	while (k --){
    		scanf ("%d%d",&n,&m);
    		printf("%d
    ",(min(n,m) - 1) * 2);
    	}
    }
    

    T2

    题目描述
    Zig和Zag正在玩文字游戏。Zig说了一个字母,而Zag说了一个以该字母开头的单词。但是这个词需要出现在给出的单词列表中,并且被是相同首字母中使用的次数最少的单词。如果单词的选择不明确(即相同首字母中使用的次数最少的单词不止一个),那么Zag会选择字典序较小的字母。输入保证对于每个Zig的字母,都有可以选择的单词。

    假设有一个由K个不同的单词组成的列表和一个Zig给出的N个字母组成的列表。编写一个程序,根据输入,输出Zag在游戏过程中说出的N个单词。
    输入
    输入格式: 第一行输入包含来自的正整数K(1≤K≤100 000)和N(1≤N≤100 000)。
    以下K行是K个单词,由小写英文字母组成,不超过21个字母。
    以下N行是Zig说的N个小写英文字母。
    输出
    N行,分别对应N个Zig的询问。
    样例
    样例输入1
    4 5
    zagreb
    split
    zadar
    sisak
    z
    s
    s
    z
    z
    样例输出1
    zadar
    sisak
    split
    zagreb
    zadar
    样例输入2
    5 3
    london
    rim
    pariz
    moskva
    sarajevo
    p
    r
    p
    样例输出2
    pariz
    rim
    pariz
    样例输入3
    1 3
    zagreb
    z
    z
    z
    样例输出3
    zagreb
    zagreb
    zagreb

    解题思路

    怎么说呢,其实就是一道模拟题,由于它查询首字母,那么你按首字母分开保存串,又要输出相等使用下字典序最小的那就可以排序。然后查一个输一个,输到底就直接回到第一个。

    代码

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<queue>
    #include<cstdlib>
    #include<vector>
    using namespace std;
    struct node {
    	char c[25];
    	bool operator < (const node &a)const {
    		if (strcmp(c,a.c) <= 0)
    			return 1;
    		else 
    			return 0;
    	}
    }s[100005];
    int k,n,pos[30],len[30],len1[30];
    char c;
    int main(){
    	//freopen("zigzag.in","r",stdin);
    	//freopen("zigzag.out","w",stdout);
    	scanf ("%d%d",&k,&n);
    	for (int i = 1;i <= k;i ++)
    		scanf ("%s",s[i].c);
    	sort(s + 1,s + 1 + k);
    	for (int i = 1;i <= k;i ++){
    		if (s[i].c[0] != s[i - 1].c[0])
    			pos[s[i].c[0] - 'a'] = i;
    		len[s[i].c[0] - 'a'] ++;
    	}
    	for (int i = 0;i <= 25;i ++)
    		len1[i] = pos[i];
    	while (n --){
    		scanf ("
    ");
    		scanf ("%c",&c);
    		int t = c - 'a';
    		printf("%s
    ",s[len1[t]].c);
    		len1[t] ++;
    		if (len1[t] - pos[t] == len[t])
    			len1[t] = pos[t];
    	}
    }
    

    T3(换了题QAQ)

    题目描述
    有n名同学要乘坐摆渡车从人大附中前往人民大学,第i位同学在第ti分钟去等车。只有一辆摆渡车在工作,但摆渡车容量可以视为无限大。摆渡车从人大附中出发、把车上的同学送到人民大学、再回到人大附中(去接其他同学),这样往返一趟总共花费m分钟(同学上下车时间忽略不计)。摆渡车要将所有同学都送到人民大学。 凯凯很好奇,如果他能任意安排摆渡车出发的时间,那么这些同学的等车时间之和最小为多少呢? 注意:摆渡车回到人大附中后可以即刻出发。
    输入
    第一行包含两个正整数n,m,以一个空格分开,分别代表等车人数和摆渡车往返一趟的时间。 第二行包含n个正整数,相邻两数之间以一个空格分隔,第i个非负整数ti代表第i个同学到达车站的时刻。
    输出
    输出一行,一个整数,表示所有同学等车时间之和的最小值(单位:分钟)。
    样例
    【样例输入1】
    5 1
    3 4 4 3 5
    【样例输出1】
    0
    【样例输入2】
    5 5
    11 13 1 5 5
    【样例输出2】
    4

    解题思路

    原题NOIP2018PJ T3 摆渡车,虽然打过一次,但忘了,主要就是DP不知道该如何定义。
    这次换了一个新的高端操作。
    为何此题难搞,关键在于它不好转移。
    首先,人的编号,我们肯定要搞到DP中去。
    然后,我们围绕这一个i号的人,必须要再定义东西,与它,与答案有关的,就只能是它等待的时间。那么,
    dp[i][j]dp[i][j] 表示第i个人等待了j分钟的最小花费(注:可能已经上车但没走)
    dp[i][j]=dp[k][l]+cost.dp[i][j] = dp[k][l] + cost.
    这便是一个最简陋的dp,看起来是四重循环,好像要炸。
    肯定,k,l,i必须要枚举,但j却是可以算出来,既然在k号人时等了l分钟就走了那么再过m分钟车就回来了,减去i号人到达的时间就算了出来。还有一个问题,就是j可能为负,表示车在等人,我们就弄0就好。即j=t[k]+l+mt[i]j = t[k] + l + m - t[i]
    cost 又该如何算呢,首先,肯定要有j,中间这一段,有i - k个人在等车,等的时间呢,都包含i等的时间。所以需要(i - k) + j这一部分。
    那么之前的等待时间呢,我们就需要另外取一个数组s[i][j]s[i][j]表示i这个人到一直到j这一个人到,中间一共的等待时间。
    预处理出来即可。
    然后处理下边界,可以打了。

    代码

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<queue>
    #include<cstdlib>
    #include<vector>
    #include<ctime>
    using namespace std;
    int n,m,t[1005],dp[505][205],s[505][505];
    int main(){
    	freopen("bus.in","r",stdin);
    	freopen("bus.out","w",stdout);
    	scanf ("%d%d",&n,&m);
    	if (m == 1){
    		printf("0
    ");
    		return 0;
    	}
    	for (int i = 1;i <= n;i ++)
    		scanf ("%d",&t[i]);
    	sort(t + 1,t + 1 + n);
    	for (int i = 1;i <= n;i ++){
    		for (int j = 1;j < i ;j ++){
    			for (int k = j;k <= i;k ++)
    				s[j][i] += t[i] - t[k];
    		}
    	}
    	t[0] = -0x3f3f3f3f;
    	memset(dp,0x3f,sizeof(dp));
    	for (int i = 0;i <= m;i ++){
    		dp[0][i] = 0;
    		dp[1][i] = i;
    	}
    	for (int i = 2;i <= n;i ++){
    		for (int j = 0;j < i;j ++){
    			for (int k = 0;k <= m;k ++){
    				int w = t[j] + k + m - t[i];
    				if (w < 0)
    					dp[i][0] = min(dp[i][0],dp[j][k] + s[j + 1][i]);
    				else 	
    					dp[i][w] = min(dp[i][w],dp[j][k] + s[j + 1][i] + (i - j) * w);
    			}
    		}
    	}
    	int ans = 0x3f3f3f3f;
    	for (int i = 0;i <= m;i ++)
    		ans = min(ans,dp[n][i]);
    	printf("%d",ans);
    }
    

    T4

    题目描述
    游戏世界中有N个楼从左到右排列,从左到右编号为1到N,第i幢楼的高度为Hi,楼上的金币数为Gi,游戏可以从任意一个楼开始且包涵几步。每一步玩家可以从当前位置向右跳(可以跳过一些楼)但必须跳到不低于当前楼的高度的楼上。他到了楼上后,可以得到楼上的金币。他可以在跳任意步(可以是零步)后结束游戏,但是要保证收到的金币数要大于等于K,现在想知道共有多少不同的种方案满足游戏。两个方案不同是指至少有一个楼不一样的方案。
    输入
    第一行两个数​N (1 ≤ ​N ≤ 40) and ​K (1 ≤ ​K ≤ 4·10​^10​ )
    接下来N行,每行两个正整数,第i行用Hi和Gi表示第i个楼的高度和上面的金币。 (1 ≤ Hi, ​Gi ≤ 109​ )
    输出
    一行一个数,表示方案总数。
    样例
    样例输入1

    4​ ​6
    2​ ​1
    6​ ​3
    7​ ​2
    5​ ​6
    样例输出1
    3
    样例输入2
    2 7
    4 6
    3 5
    样例输出2
    0
    样例输入3
    4 15
    5 5
    5 12
    6 10
    2 1
    样例输出3
    4

    解题思路

    看到这道题,一开始想dp,后面发现不对,立马搜索,2^20虚都不虚。然而没开LL
    可以打搜索剪枝,但我真的想不到怎么剪,有大佬知道可以评论告诉一下。
    正解折半搜索,把搜到的所有的情况全部储存(真暴力)。
    再枚举啊,尺取啊,操作一下即可。

    代码

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<queue>
    #include<cstdlib>
    #include<vector>
    using namespace std;
    struct node {
    	int h;
    	long long s;
    	node (long long S,int H){
    		s = S,h = H;
    	}
    };
    struct node1 {
    	int x,pos;
    	bool operator < (const node1 &a)const {
    		return x < a.x;
    	}
    }s[45];
    int n,mid,h[45],tot,cnt[45];
    long long k,ans,g[45];
    vector <node>L;
    vector <node>R;
    void dfs(int step,long long sum,int h1){
    	L.push_back(node(sum,h1));
    	if (step == mid)
    		return ;
    	for (int i = step + 1;i <= mid;i ++)
    		if (h[i] >= h1)
    			dfs(i,sum + g[i],h[i]);
    }
    void dfs1(int step,long long sum,int h1,int h2){
    	R.push_back(node(sum,h2));
    	if (step == n)
    		return ;
    	for (int i = step + 1;i <= n;i ++)
    		if (h[i] >= h1)
    			dfs1(i,sum + g[i],h[i],h2?h2:h[i]);
    }
    bool cmp(node a,node b){
    	return a.s > b.s;
    }
    bool cmp1(node a,node b){
    	return a.s < b.s;
    }
    int main(){
    	//freopen("san.in","r",stdin);
    	//freopen("san.out","w",stdout);
    	scanf ("%d%lld",&n,&k);
    	for (int i = 1;i <= n;i ++){
    		scanf ("%d%lld",&h[i],&g[i]);
    		s[i].x = h[i];
    		s[i].pos = i;
    	}
    	sort(s + 1 ,s + 1 + n);
    	for (int i = 1;i <= n;i ++){
    		if (s[i].x != s[i - 1].x)
    			h[s[i].pos] = ++ tot;
    		else 
    			h[s[i].pos] = tot;
    	}
    	mid = n / 2;
    	dfs(0,0,0);
    	dfs1(mid,0,0,0);
    	sort(L.begin(),L.end(),cmp);
    	sort(R.begin(),R.end(),cmp1);
    	R[0].h = 41;
    	for (int i = 0;i < R.size();i ++)
    		cnt[R[i].h] ++;
    	int i = 0,j = 0;
    	while (1){
    		while (j < R.size() && R[j].s + L[i].s < k){
    			cnt[R[j].h] --;
    			j ++;
    		}
    		if (R[j].s + L[i].s < k)
    			break;
    		ans += R.size() - j;
    		for (int k1 = 1;k1 <= n;k1 ++)
    			if (h[k1] < L[i].h)
    				ans -= cnt[h[k1]];
    		i ++;
    		if (i == L.size())
    			break;
    	}
    	printf("%lld
    ",ans);
    }
    /*
    4 6
    2 1
    6 3
    7 2
    5 6
    */
    

    T5

    题目描述
    给一棵N个节点的树,编号从1到N,再给定m对点(u,v),你要将树上的每条无向边变为有向边,使得给定的点对都满足u能到达v或v能到达u。问有多少种不同的方案,答案对(1e9+7)求余。
    输入
    第一行两个正整数​N and ​M(1 ≤ ​N, M≤ 3*1e5​ ),表示树的结点个数,和点对的个数。

    接下来N-1行,每行两个整数,表示树上的边。

    接下来M行,每行两个不同的正整数(ai,bi),表示对应的点对,点对互不相同。
    输出
    一行一个数,表示不同的方案数模1e9+7

    20%的数据树是一个链,即第i个点连在i+1上。

    40%的数据N,M≤​ ​5*1e3​ .
    样例
    样例输入1

    4 1
    1 2
    2 3
    3 4
    2 4
    样例输出1
    4
    样例输入2
    7 2
    1 2
    1 3
    4 2
    2 5
    6 5
    5 7
    1 7
    2 6
    样例输出2
    8
    样例输入3
    4 3
    1 2
    1 3
    1 4
    2 3
    2 4
    3 4
    样例输出3
    0

    解题思路

    肯定的,树上路径有关的题直接搞LCA,同时肯定要并一块,用并查集,并且,正反两个乘法原理又与2的次方有关。然而我没管,直接搞20分的,结果炸了,输出0有近三分之一的分数。我…
    正解还没码

  • 相关阅读:
    【阿里的感悟】质量该如何做? .(转载)
    java linux 配置环境
    Spring Bean属性绑定Bean返回值
    Spring BeanNameAutoProxyCreator 与 ProxyFactoryBean
    Spring Aop之(二)Aop 切面声明和通知
    Ubuntu开机自动启动Script
    转战博客园!
    linux 系统管理11 ——系统安全及应用
    linux awk
    Rsync数据同步工具
  • 原文地址:https://www.cnblogs.com/lover-fucker/p/13566661.html
Copyright © 2011-2022 走看看