2020.3.28-ICPC训练联盟周赛,选用试题:UCF Local Programming Contest 2016
A.Majestic 10
签到题。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<bitset>
#include<cassert>
#include<cctype>
#include<cmath>
#include<cstdlib>
#include<ctime>
#include<deque>
#include<iomanip>
#include<list>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<vector>
#include <string>
using namespace std;
typedef long long ll;
int main()
{
ios::sync_with_stdio(false);
int n,a,b,c;
cin>>n;
while(n--)
{
int sum=0;
cin>>a>>b>>c;
cout<<a<<" "<<b<<" "<<c<<endl;
if(a%10==0&&a)sum++;
if(b%10==0&&b)sum++;
if(c%10==0&&c)sum++;
if(sum==0)cout<<"zilch"<<endl;
else if(sum==1)cout<<"double"<<endl;
else if(sum==2)cout<<"double-double"<<endl;
else cout<<"triple-double"<<endl;
cout<<endl;
}
}
B.Phoneme Palindromes
将所有可以相互替换的字母都统一替换成相同字母,然后判断回文即可。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<bitset>
#include<cassert>
#include<cctype>
#include<cmath>
#include<cstdlib>
#include<ctime>
#include<deque>
#include<iomanip>
#include<list>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<vector>
#include <string>
using namespace std;
typedef long long ll;
string str[110];
int main()
{
ios::sync_with_stdio(false);
int T,n,i,cas=0,j;
char s,s1;
cin>>T;
while(T--)
{
map<char,char>mp;
cin>>n;
for(i=0;i<26;i++)
{
char c='a'+i;
mp[c]=c;
}
for(i=0;i<n;i++)
{
cin>>s>>s1;
mp[s]=mp[s1]=s;
}
cin>>n;
for(i=0;i<n;i++)
cin>>str[i];
cout<<"Test case #"<<++cas<<":"<<endl;
for(i=0;i<n;i++)
{
cout<<str[i]<<" ";
int fl=0;
int len=str[i].length();
for(j=0;j<len;j++)str[i][j]=mp[str[i][j]];
for(j=0;j<len/2;j++)
if(str[i][j]!=str[i][len-j-1])
{
fl=1;
break;
}
if(fl==0)cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
cout<<endl;
}
}
C.Don’t Break the Ice
每敲掉一个冰块,改变其状态后判断与之相关的行列上的所有冰块,若有掉落的冰块(加入队列)继续改变判断即可。
#include<iostream>
#include<cstdio>
#include<set>
#include<queue>
#include<stack>
#include<vector>
#include<bitset>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=1e5+10;
inline int read(){
int x=0,y=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-'){
y=-1;
}
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*y;
}
int a[52][52];//表示第[i][j]冰块状态
int b[52][52];//表示第[i][j]冰块行上冰块数
int c[52][52];//表示第[i][j]冰块列上冰块数
queue<int> p,q;
void bfs(int x,int y,int n){
p.push(x),q.push(y);
while(!p.empty()){
x=p.front();
y=q.front();
p.pop(),q.pop();
a[x][y]=1;//标记冰块掉落 //接着对该冰块掉落后所影响的其他冰块进行判断
for(int i=1;i<=n;i++){
b[i][y]++;
if(c[i][y]&&!a[i][y]){
p.push(i),q.push(y);
}
c[x][i]++;
if(b[x][i]&&!a[x][i]){
p.push(x),q.push(i);
}
}
}
return;
}
int main(){
int T=read();
for(int t=1;t<=T;t++){
int ans=0;
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
memset(c,0,sizeof(c));
int n=read(),m=read();
for(int i=0;i<m;i++){
int x=read(),y=read(); if(a[x][y]){
ans++;
//倘若冰块已掉落则答案加1
}
else{
bfs(x,y,n);
//未掉落则标记其掉落
}
}
printf("Strategy #%d: ",t);
printf("%d\n",ans);
printf("\n");
}
return 0;
}
D.Loopy Word Search
存放每一个大写字母开头的位置,对每一串直接暴力搜索匹配即可(注意字符串可循环匹配,见样例2第2个字符串)
#include<iostream>
#include<cstdio>
#include<set>
#include<queue>
#include<stack>
#include<vector>
#include<bitset>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=1e5+10;
inline int read(){
int x=0,y=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-'){
y=-1;
}
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*y;
}
string s[110];
typedef struct NODE{
int x,y;
}N;
vector<N> a[26];//存储每个字母的位置
int main(){
int T=read();
for(int t=1;t<=T;t++){
for(int i=0;i<26;i++){
a[i].clear();
}
int n=read(),m=read();
for(int i=0;i<n;i++){
cin>>s[i];
for(int j=0;j<m;j++){
N tmp;
tmp.x=i,tmp.y=j;
a[s[i][j]-'A'].push_back(tmp);
}
}
printf("Word search puzzle #%d:\n",t);
int p=read();
string q;
for(int i=0;i<p;i++){
cin>>q;
int cnt=a[q[0]-'A'].size(); //从所需查询字符串的开头字母开始循环
for(int j=0;j<cnt;j++){ N tmp=a[q[0]-'A'][j];
int len=q.length();
int flag2=1;
//查询字母向上方向匹配情况
for(int e=(tmp.x+2*n-1)%n,f=1;f<len;f++,e=(e+2*n-1)%n){
if(q[f]!=s[e][tmp.y]){
flag2=0;
break;
}
}
if(flag2){
printf("U %d %d ",tmp.x+1,tmp.y+1);
cout<<q<<endl;
break;
}
flag2=1;
//查询字母向下方向匹配情况
for(int e=(tmp.x+1)%n,f=1;f<len;f++,e=(e+1)%n){
if(q[f]!=s[e][tmp.y]){
flag2=0;break;
}
}
if(flag2){
printf("D %d %d ",tmp.x+1,tmp.y+1);
cout<<q<<endl;
break;
}
flag2=1;
//查询字母向左方向匹配情况
for(int e=(tmp.y+2*m-1)%m,f=1;f<len;f++,e=(e+2*m-1)%m){
if(q[f]!=s[tmp.x][e]){
flag2=0;break;
}
}
if(flag2){
printf("L %d %d ",tmp.x+1,tmp.y+1);
cout<<q<<endl;
break;
}
//查询字母向右方向匹配情况
flag2=1;
for(int e=(tmp.y+1)%m,f=1;f<len;f++,e=(e+1)%m){
if(q[f]!=s[tmp.x][e]){
flag2=0;break;
}
}
if(flag2){
printf("R %d %d ",tmp.x+1,tmp.y+1);
cout<<q<<endl;
break;
}
}
}
printf("\n");
}
return 0;
}
E.Wildest Dreams
按照题目意思直接模拟即可(注意路段时间结束,女儿刚好离开时,歌曲按顺序播放,而不会到开头重复)
#include<iostream>
#include<cstdio>
#include<set>
#include<queue>
#include<stack>
#include<vector>
#include<bitset>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=1e5+10;
inline int read(){
int x=0,y=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-'){
y=-1;
}
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*y;
}
int a[110];//代表每首歌曲播放时间
int main(){
int T=read();
for(int t=1;t<=T;t++){
int n=read(),m=read();
int it1=0,it2=0,sum=0;
//it1为女儿喜欢歌曲开始时间
//it2为女儿喜欢歌曲结束时间
//sum为cd所有歌曲播放总时间
for(int i=1;i<=n;i++){
a[i]=read();
sum+=a[i];
if(i<m) {
it1+=a[i];
}
}
it2=it1+a[m];
printf("CD #%d:\n",t); int p=read();
for(int k=1;k<=p;k++){
int ans=0,it=0;
//it表示当前时间
int q=read();
for(int i=1;i<=q;i++){
int cnt=read();
if(cnt==0){
continue;
}
//当女儿在车上时答案直接加上当前路段时间
if(i%2==1){
ans+=cnt;
if(cnt%(it2-it1)==0){
it=it2;
}
else{
it=it1+cnt%(it2-it1);
}
}
//计算女儿不再车上时所听歌曲时间
else{
ans+=(it2-it1)*(cnt/sum);
cnt%=sum;
if(cnt<=it2-it){
ans+=cnt;
}
else{
ans+=it2-it;
if(cnt>=sum-it+it1){
ans+=cnt-(sum-it+it1);
}
}
it=(it+cnt)%sum;
}
}
printf("%d\n",ans);
}
printf("\n");
}
return 0;
}
F.Dot the i’s and Cross the T’s
这是一道普通的计算几何题,一种可行的思路如下:
二重循环枚举任意两个点构成的线段,把该线段作为线段
AB。
枚举所有的点,判断该点是否是线段
AB 的中点,如果是,则该点作为点
M。(由
于没有重合的点,所以对于每条线段
AB,点 M 最多只有一个)
如果找到了点 M,再次枚举所有的点,寻找点
C 是否存在。判断条件:|AB| = |CM|
且 AB ⊥ CM
注:
1. 判断中点的条件,点
M 在线段 AB 上且 |AM| = |BM|
2. 判断垂直可以用向量的点积等于 0 判断
3. 枚举任意两点构成的线段时间复杂度为
O(N2),由于点 M 最多只有一个,所
以枚举点 C 最多只会执行一次,总体时间100×503=12,500,000
#include <bits/stdc++.h>
using namespace std;
const double eps = 1e-8;
const double inf = 1e20;
const double pi = acos(-1.0);
const int maxp = 1010;
// 精度三态函数
int sgn(double x) {
if(fabs(x) < eps) return 0;
if(x < 0) return -1;
else return 1;
}
struct Point {
double x, y;
Point() {}
Point(double _x,double _y) {
x = _x;
y = _y;
}
void input() {
scanf("%lf%lf", &x, &y);
}
void output() {
printf("%.2f %.2f\n", x, y);
}
bool operator == (Point b)const {
return sgn(x-b.x) == 0 && sgn(y-b.y) == 0;
}
bool operator < (Point b)const {
return sgn(x-b.x)== 0?sgn(y-b.y)<0:x<b.x;
}
Point operator -(const Point &b)const {
return Point(x-b.x,y-b.y);
} // 向量叉积
double operator ^(const Point &b)const {
return x*b.y - y*b.x;
} // 向量点积
double operator *(const Point &b)const {
return x*b.x + y*b.y;
} double len() {
return hypot(x,y);
}
double len2() {
return x*x + y*y;
} // 当前点到点 p 的距离
double distance(Point p) {
return hypot(x-p.x,y-p.y);
}
Point operator +(const Point &b)const{
return Point(x+b.x,y+b.y);
}
Point operator *(const double &k)const{
return Point(x*k,y*k);
}
Point operator /(const double &k)const{
return Point(x/k,y/k);
} };
typedef Point Vector; // 向量
Point p[maxp]; // 输入的点
struct Line{
Point s,e;
Line(){}
Line(Point _s,Point _e){
s = _s;
e = _e;
}
bool operator ==(Line v){
return (s == v.s)&&(e == v.e);
}
void input(){
s.input();
e.input();
}
// 线段长度
double length(){
return s.distance(e);
}
// 点 p 是否在当前线段上
bool pointonseg(Point p){
return sgn((p-s)^(e-s)) == 0 && sgn((p-s)*(p-e)) <= 0;
} };
int main() {
int T;
scanf("%d", &T);
int kase = 0;
while(T--) {
int n;
scanf("%d", &n);
for(int i = 0; i < n; ++i) {
p[i].input();
}
int ans = 0; for(int i = 0; i < n; ++i) {
for(int j = i + 1; j < n; ++j) {
Line tmp = Line(p[i], p[j]); // 点 p[i] 和点 p[j] 构成线段 AB
for(int k = 0; k < n; ++k) { // 寻找中点 M
if(k == j || k == i) continue;
// 点 p[k] 在线段上,且点 p[k] 到点 p[i] 的距离与点 p[k] 到点 p[j] 的距离相等
if(tmp.pointonseg(p[k]) && sgn(p[k].distance(p[i]) - p[k].distance(p[j])) == 0) {
for(int l = 0; l < n; ++l) { // 寻找点 C
if(l == k || l == j || l == i) continue;
Line l1 = Line(p[i], p[j]); // 点 p[i] 与点p[j] 构成的线段,也就是线段 AB
Line l2 = Line(p[k], p[l]); // 点 p[k] 与点p[l] 构成的线段,也就是线段 MC
Vector v1 = p[i] - p[j]; // 点 p[i] 指向点 p[j] 的向量,也就是向量 AB
Vector v2 = p[k] - p[l]; // 点 p[k] 指向点 p[l] 的向量,也就是向量 MC
// l1 与 l2 的长度相等,向量 v1 与 v2 的点积为 0 (代表垂直)
if(sgn(l1.length() - l2.length()) == 0 && sgn(v1 * v2) == 0) {
++ans;
}
}
}
}
}
}
printf("Set #%d: %d\n\n", ++kase, ans);
}
return 0;
}
J.Jedi and the Galactic Empire
动态规划,对于每一颗子弹分为用武士1挡或者用武士2挡,同时记录下抵挡时已经抵挡的子弹颗数,以及武士1和武士2下一次可以抵挡的时间。
假设武士1的时间间隔为10,武士2的时间间隔为7
子弹到达的时间 |
0 |
2 |
4 |
8 |
13 |
13 |
|
武士1 抵挡 |
已抵挡子弹数az[0] |
0 |
1 |
2 |
2 |
3 |
4 |
武士1下一次抵挡时刻az[1] |
0 |
12 |
14 |
18 |
23 |
23 |
|
武士2下一次抵挡时刻az[2] |
0 |
0 |
9 |
9 |
11 |
20 |
|
武士2 抵挡 |
已抵挡子弹数az[3] |
0 |
1 |
2 |
2 |
3 |
4 |
武士1下一次抵挡时刻az[4] |
0 |
0 |
12 |
12 |
12 |
23 |
|
武士2下一次抵挡时刻az[5] |
0 |
9 |
11 |
15 |
20 |
20 |
每次往前找寻最优的上一次抵挡时刻时,满足当前时刻大于对应的武士的下一次抵挡时刻,已抵挡的子弹数最多,且另一个武士的抵挡时刻最小作为当前的数值。
例如第一次的13时刻的武士2抵挡,当前时刻为13也就是时刻4的两次抵挡以及时刻8的武士1抵挡,都满足当前时刻大于武士2下一次抵挡时刻,且子弹数最大为2,在此基础上时刻4的武士2抵挡的武士1下一次抵挡时刻最小,因此选择这一项作为上一次的抵挡,此项的已抵挡的子弹数加一,武士一的下一次抵挡时刻不变,武士2的下一次抵挡时刻更新为当前时刻加上武士2的时间间隔为最优。
最后记录下抵挡子弹数的最大数即可
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<stdlib.h>
#include<cmath>
#include<string>
#include<vector>
#define ll long long int
#define lll unsigned long long
const int INF=0x7fffffff;
const ll LNF=0x7fffffffffffffff;
const int MAX = 6005;
using namespace std;
ll ac[1005],ax[2],az[6][1005];//ac记录子弹到达时间,ax记录武士的格挡的时间差,az为dp的表格,具体见上表
int main(){
ll nn,kk=0,k,n,m,x,l,i,v,mx,mm,ma;
scanf("%lld",&nn);
while(kk<nn){
scanf("%lld",&n);
k=1;ma=0;
while(k<=n){
scanf("%lld",&ac[k]);
k++;
}
sort(ac+1,ac+n+1);//对子弹到达时间排序
k=0;
scanf("%lld",&m);
while(k<m){
scanf("%lld",&ax[k]);
k++;
}
k=0;
while(k<6){
az[k][0]=0;//对于0时刻各项均为0
k++;
}
k=1;
while(k<=n){//遍历每一个子弹到达的时刻
x=ac[k];
l=0;
while(l<m){//遍历这颗子弹由武士1还是武士2格挡
v=0;mx=0;
while(v<m){
i=0; //遍历之前的每一种情况
while(i<k){
if(az[3*v+l+1][i]<=x&&az[3*v][i]>mx){//找到满足对应的武士下一次抵挡时刻小于当前时刻,并且已抵挡子弹数最多的子弹数的数量
mx=az[3*v][i];
}
i++;
}
v++;
}
v=0;mm=LNF;
while(v<m){
i=0; //再次遍历之前的每一种情况
while(i<k){
if(az[3*v][i]==mx&&az[3*v+l+1][i]<=x&&mm>az[3*v-l+2][i]){//找到满足对应的武士下一次抵挡时刻小于当前时刻,并且已抵挡子弹数等于最多的子弹数,同时另一个武士的下一次抵挡时刻最小的情况
mm=az[3*v-l+2][i];
}
i++;
}
v++;
}
az[3*l][k]=mx+1;//当前已抵挡子弹数为之前已抵挡最多子弹数加1
ma=max(ma,az[3*l][k]);//ma记录下所有情况中已抵挡的最多的子弹数数量
if(l==0){//如果为武士1抵挡,武士1的下一次抵挡时刻为当前时刻加上武士1时间间隔,武士2下一次抵挡时刻不变
az[1][k]=x+ax[0];
az[2][k]=mm;
}
else{//如果为武士2抵挡,武士2的下一次抵挡时刻为当前时刻加上武士2时间间隔,武士1下一次抵挡时刻不变
az[4][k]=mm;
az[5][k]=x+ax[1];
}
l++;
}
k++;
}
kk++;
printf("Mission #%lld: %lld\n\n",kk,n-ma);//结果为所有子弹数减去已抵挡的最多的子弹数
}
return 0;
}
H.Count the Dividing Pairs
由于整数的重复出现,若A出现了x次,而B出现了y次,且(A,B)为整除对,可以发现形如(A,B)这样的整除对共有x*y对,用数组来记录数字出现的次数,数组记为count。暴力得到所有整除对(A,B) count[A]*count[B]的累加和即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int vis[10000001];
int main()
{ int T,cas=0;
ll n,a,i,j;
scanf("%d",&T);
while(T--)
{
ll ma=0;
memset(vis,0,sizeof(vis));
scanf("%lld",&n);
for(i=0;i<n;i++)
{
scanf("%lld",&a);
vis[a]++;
ma=max(ma,a);
}
printf("Test case #%d: ",++cas);
ll ans=0; //0是所有数的倍数,所有数都是1的倍数
ans=(ll)vis[0]*(n-vis[0])+(ll)vis[1]*(n-vis[0]-vis[1]); //循环找出所有的倍数关系
for(i=2;i<=ma/2;i++)
{
if(vis[i])
{
for(j=i+i;j<=ma;j+=i)
if(vis[j])ans+=(ll)vis[i]*(ll)vis[j];
}
}
printf("%lld\n\n",ans); }
}
I.Lineup the Dominoes
动态规划。dp[mask][last][orientation],表示使用mask指示的子数组,以第last个多米诺骨牌为结尾的合法排列的方法,orientation多米诺骨牌的状态,0表示原来的方向,1表示翻转。如果mask使用二进制表示为01010101,表示使用第0,2,4,6个多米诺骨牌排列而成,即二进制的每一位代表原数组的一个位置,1代表使用这个位置上的数组,0代表不使用。
如果i和last都是原来的方向:
dp[mask][last][0] =sum
(dp[mask][last][0],dp[premask][i][0])。
如果i是翻转的,last是原来的方向:
dp[mask][last][0] =sum
(dp[mask][last][0],dp[premask][i][1])。
如果i是原来的方向,last是翻转的方向:
dp[mask][last][1] =sum (dp[mask][last][1],dp[premask][i][0])。
如果i和last都是翻转的方向:
dp[mask][last][1] =sum (dp[mask][last][1],dp[premask][i][1])。
last表示mask所代表的的子集合法排列的最后一个骨牌,premask是mask除去第last个骨牌之后的子集,i表示premask所代表的的子集合法排列的最后一个骨牌。最后只要将所有dp[1<<n-1][i][0]和dp[1<<n-1][i][1]累加所得即可(当s[i]==t[i]时不用加dp[1<<n-1][i][1])。
特殊情况——如果所有的多米诺骨牌都是一样的,那么所有的顺序都是有效的,即全排列。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
int s[20],t[20];
ll p[25];
ll dp[70000][20][2];
int main()
{ int T,i,mask,last,n;
p[0]=1;
for(i=1;i<=20;i++)p[i]=p[i-1]*i%mod;
scanf("%d",&T);
while(T--)
{
int fl=0;
scanf("%d",&n);
for(i=0;i<n;i++)
scanf("%d%d",&s[i],&t[i]);
for(i=1;i<n;i++)
if((s[i]==s[0]&&t[i]==t[0])||(s[i]==t[0]&&t[i]==s[0])){}
else {fl=1;break;} // 特殊情况——如果所有的多米诺骨牌都是一样的,那么所有的顺序都是有效的。
if(fl==0)
{
printf("%lld\n",p[n]);
continue;
} memset(dp,0,sizeof dp); //初始化,表示每个骨牌都有一个初始状态
for(i=0;i<n;i++)
dp[1<<i][i][0]=dp[1<<i][i][1]=1; for(mask=3;mask<(1<<n);mask++)
{
for(last=0;last<n;last++)
{
if((mask&(1<<last))==0)continue;
int premask=mask-(1<<last); for(i=0;i<n;i++)
{
if((premask&(1<<i))==0)continue; //i和last都是原来的方向
if(t[i]==s[last])
dp[mask][last][0] = (dp[mask][last][0]+dp[premask][i][0])%mod;
//i是翻转的,last是原来的方向
else if(s[i]==s[last])
dp[mask][last][0] = (dp[mask][last][0]+dp[premask][i][1])%mod; //i是原来的方向,last是翻转的方向
if(t[i]==t[last])
dp[mask][last][1] = (dp[mask][last][1]+dp[premask][i][0])%mod;
//i和last都是翻转的方向
else if(s[i]==t[last])
dp[mask][last][1] = (dp[mask][last][1]+dp[premask][i][1])%mod;
}
}
} //计算所有可能的情况
ll ans=0;
for(i=0;i<n;i++)
{
ans=(ans+dp[(1<<n)-1][i][0])%mod;
if(s[i]!=t[i])//特殊情况不再统计
ans=(ans+dp[(1<<n)-1][i][1])%mod;
}
printf("%lld\n",ans);
}
}
J.Rising Tides
显然我们要采用二分的方法来寻找答案,给定一个高度如果能确定在这个高度时是否可以安全到达终点,那我们就可以很快二分出最大可行的高度。在判断一个高度是否可行时,搜索从起点开始,在限制的高度下所有可以到达的坐标位置,检验是否经过终点即可。
#include<bits/stdc++.h>
using namespace std;
int t,n,m,a[511][511];
int dr[4]={1,0,-1,0};
int dc[4]={0,1,0,-1};
int inf = 1000000000;
bool visited[511][511];
bool check(int height)//检查当前高度是否能到达终点
{
for(int i=0;i<n;i++)//将所有点设置成未访问过
for(int j=0;j<m;j++)
visited[i][j]=0;
queue <int> q;
q.push(0);
q.push(0);
q.push(0);
visited[0][0]=1;
while(q.size()>0)
{
int row=q.front();//用队列记录行列坐标和当前的高度
q.pop();
int col=q.front();
q.pop();
int dist =q.front();
q.pop();
if(a[row][col]-dist>=height)
{
if(row==n-1&&col==m-1)//如果可以到达终点则返回true
{
return 1;
}
for(int i = 0; i < 4; i++)//否则往四个方向上尝试
{
int nr = row + dr[i];
int nc = col + dc[i]; if(nr>=0&&nr<n&&nc>=0&&nc<m&&!visited[nr][nc])//当位置没有超出范围并且没有访问过时说明下个点可行 压入队列
{
visited[nr][nc] = 1;
q.push(nr);
q.push(nc);
q.push(dist + 1);
}
}
}
}
return 0;
}
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
scanf("%d",&a[i][j]);
int low=0,high=inf;//二分法查找答案
while(low < high)
{
int mid=(low+high+1)/2;
if(check(mid))
low = mid;
else
high = mid - 1;
}
if(low>0)
printf("%d\n",low);
else
printf("impossible\n"); }
return 0;
}
K.Bouncing Bunnies
Connie和Ronnie可以在 |TA - TB|=|HA - HB|的山峰之间跳跃,可以转化为可以在TA - HA= TB - HB或TA + HA=TB + HB之间的山峰跳跃,构建一个图,节点是(T + H)的值和(T - H)的值(每个不同的值对应一个节点),将山丘视为其和(T + H)值与差(T - H)值之间的边,运行宽度优先搜索(或其他最短路径算法),从第一个山的和和差值开始,当我们到达最后一座山的和或差值时,我们离最后一座山只有一跳,所以答案是1 + min(最后一个和的最短路径,最后一个差的最短路径)。
#include<bits/stdc++.h>
using namespace std;
const int INF=987654321;
const int MAXN=500005;
int t[500005],h[500005];
struct qnode
{
int v;
int c;
qnode(int _v=0,int _c=0):v(_v),c(_c){}
bool operator <(const qnode &r)const
{
return c>r.c;
}
};
struct Edge
{
int v,cost;
Edge(int _v=0,int _cost=0):v(_v),cost(_cost){}
};
vector<Edge>E[MAXN];
bool vis[MAXN];
int dist[MAXN];
void Dijkstra(int n,int start1,int start2)//最短路,初始化到点start1和点start2的距离为0
{
memset(vis,false,sizeof(vis));
for(int i=1;i<=n;i++)dist[i]=INF;
priority_queue<qnode>que;
while(!que.empty())que.pop();
dist[start1]=0;
que.push(qnode(start1,0));
dist[start2]=0;
que.push(qnode(start2,0));
qnode tmp;
while(!que.empty())
{
tmp=que.top();que.pop();
int u=tmp.v;
if(vis[u])continue;
vis[u]=true;
for(int i=0;i<E[u].size();i++)
{
int v=E[tmp.v][i].v;
int cost=E[u][i].cost;
if(!vis[v]&&dist[v]>dist[u]+cost)
{
dist[v]=dist[u]+cost;
que.push(qnode(v,dist[v]));
}
}
}
}
void addedge(int u,int v,int w)//加边
{
E[u].push_back(Edge(v,w));
}
int main()
{
int n,m,i,u,v,w,T;
int kk=0;
scanf("%d",&T);
while(T--)
{
map<int,int>mp;
m=0;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&t[i]);
for(int i=1;i<=n;i++)
scanf("%d",&h[i]);
for(int i=1;i<=n;i++)//给所有不同的T+H和T-H加序号
{
if(mp[t[i]+h[i]]==0)
mp[t[i]+h[i]]=++m;
if(mp[t[i]-h[i]]==0)
mp[t[i]-h[i]]=++m;
}
for(int i=1;i<=m;i++)E[i].clear();
for(int i=1;i<=n;i++)//给每一个山的T+H和T-H值对应的点加边
{
addedge(mp[t[i]+h[i]],mp[t[i]-h[i]],1);
addedge(mp[t[i]-h[i]],mp[t[i]+h[i]],1);
}
Dijkstra(m,mp[t[1]+h[1]],mp[t[1]-h[1]]);
int ans=min(dist[mp[t[n]+h[n]]],dist[mp[t[n]-h[n]]]);//选取到最后一座山的T-H和T+H中较小的距离
if(ans>=987654321)
printf("Field #%d: -1\n\n",++kk);//跳不到最后一座山
else
printf("Field #%d: %d\n\n",++kk,ans+1);//加1跳到最后一座山
}
}
2020.3.28-ICPC训练联盟周赛,选用试题:UCF Local Programming Contest 2016的更多相关文章
- ICPC训练联盟周赛Preliminaries for Benelux Algorithm Programming Contest 2019
I题 求 a 数组平方的前缀和和求 a 数组后缀和,遍历一遍即可 AC代码 #include<iostream>#include<cmath>using namespace s ...
- 03.28,周六,12:00-17:00,ICPC训练联盟周赛,选用试题:UCF Local Programming Contest 2016正式赛。
A. Majestic 10 题意:三个数均大于10则输出"triple-double",如果两个数大于10则输出"double-double",如果一个大于1 ...
- 03.14 ICPC训练联盟周赛,Preliminaries for Benelux Algorithm Programming Contest 2019
A .Architecture 题意:其实就是想让你找到两行数的最大值,然后比较是否相同,如果相同输出'possible',不同则输出'impossible' 思路:直接遍历寻找最大值,然后比较即可 ...
- 03.21 ICPC训练联盟周赛:UCF Local Programming Contest 2018正式赛
B Breaking Branches 题意:两个人比赛折枝,谁剩下最后1,无法折出整数即为输 思路:树枝长n,若是奇数,则Bob胜出,若是偶数,则Alice胜出,且需要输出1: 1 #include ...
- 2020.3.14--训练联盟周赛 Preliminaries for Benelux Algorithm Programming Contest 2019
1.A题 题意:给定第一行的值表示m列的最大值,第m行的值表示n行的最大值,问是否会行列冲突 思路:挺简单的,不过我在一开始理解题意上用了些时间,按我的理解是输入两组数组,找出每组最大数,若相等则输出 ...
- 计蒜客 28449.算个欧拉函数给大家助助兴-大数的因子个数 (HDU5649.DZY Loves Sorting) ( ACM训练联盟周赛 G)
ACM训练联盟周赛 这一场有几个数据结构的题,但是自己太菜,不会树套树,带插入的区间第K小-替罪羊套函数式线段树, 先立个flag,BZOJ3065: 带插入区间K小值 计蒜客 Zeratul与Xor ...
- 计蒜客 ACM训练联盟周赛 第一场 Christina式方格取数 思维
助手Christina发明了一种方格取数的新玩法:在n*m的方格棋盘里,每个格子里写一个数.两个人轮流给格子染色,直到所有格子都染了色.在所有格子染色完后,计算双方的分数.对于任意两个相邻(即有公共边 ...
- 计蒜客 ACM训练联盟周赛 第一场 从零开始的神棍之路 暴力dfs
题目描述 ggwdwsbs最近被Zeratul和Kyurem拉入了日本麻将的坑.现在,ggwdwsbs有13张牌,Kyurem又打了一张,加起来有14张牌.ggwdwsbs想拜托你帮他判断一下,这14 ...
- 计蒜客 ACM训练联盟周赛 第一场 Alice和Bob的Nim游戏 矩阵快速幂
题目描述 众所周知,Alice和Bob非常喜欢博弈,而且Alice永远是先手,Bob永远是后手. Alice和Bob面前有3堆石子,Alice和Bob每次轮流拿某堆石子中的若干个石子(不可以是0个), ...
随机推荐
- 微信小游戏 Three.js UI 2D text 简单方案
在微信小游戏中使用 THREE.js 引擎,没有合适的 UI 库可用,只能自己动手.图片啥的都还好,text 不好弄.text 要计算 width 和 height,不然事件响应范围不对. funct ...
- Vue 2.0 与 Vue 3.0 响应式原理比较
Vue 2.0 的响应式是基于Object.defineProperty实现的 当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 prop ...
- Robot Framework(14)- Variables 表的详细使用和具体例子
如果你还想从头学起Robot Framework,可以看看这个系列的文章哦! https://www.cnblogs.com/poloyy/category/1770899.html Variable ...
- MongoDB(2)- 安装 MongoDB
MacOS 安装 MongoDB 博主目前都用 mac 电脑练习,所以这里重点讲 MacOS 安装方式 系统要求 MongoDB 4.4 社区版支持 macOS 10.13 或更高版本 安装 Home ...
- Linux处理二进制文件工具
处理目标文件的工具 在Linux系统中有大量可用的工具可以帮助我们理解和处理目标文件.特别地,GNU binutils包尤其有帮助,而且可以运行在每一个Linux平台上 序号 命令 说明 1 AR 创 ...
- TreeView和ListView数据库查询数据联动操作
好久不用了,重新整理下放这里以备需要使用,功能见图 数据库表结构 定义TreeView addObject中data存储的记录集 type PNode = ^TNode; TNode = record ...
- ysoserial CommonsColletions3分析(2)
上篇文章讲到CC3的TransformedMap链,这篇我们就来讲一下LazyMap链. 其实LazyMap链还是使用的TemplatesImpl承载payload,InstantiateTransf ...
- nmap使用命令(转载)原文地址https://www.jianshu.com/p/4030c99fcaee
- 边缘使用 K8s 门槛太高?OpenYurt 这个功能帮你快速搭建集群!
OpenYurt作为阿里巴巴首个开源的边缘云原生项目,涉及到边缘计算和云原生两个领域.然而,许多边缘计算的开发者并不熟悉云原生相关的知识.为了降低 OpenYurt 的使用门槛,帮助更多地开发者快速上 ...
- Alex网络结构
AlexNet网络结构 网络包含8个带权重的层:前5层是卷积层,剩下的3层是全连接层.最后一层全连接层的输出是1000维softmax的输入,softmax会产生1000类标签的分布网络包含8个带 ...