VK Cup 2017 - Round 1

传送门

A. Bear and Friendship Condition(思维or完全图判定)

•题意

给你n个人,m个朋友关系

朋友是会传递的,若A B是朋友,A C是朋友,则必须有B C的朋友关系

符合这个关系输出YES,否则输出NO

•思路

n个人,但凡是有朋友关系的,必定在同一个朋友圈内

所以可以分成若干个朋友圈

在一个朋友圈内部,若符合条件肯定是互为朋友

也就是 是一个完全图

接下来就是判断是否是完全图了

举个栗子:1234在一个朋友圈内,且符合条件

则人与朋友的对应关系为

1与1 2 3 4为朋友

2与1 2 3 4为朋友

3与1 2 3 4为朋友

4与1 2 3 4为朋友

即,他们的朋友是完全相同的!

挨个去判断朋友是否相同显然时间复杂度不够优雅

只需要排序后,看第一个朋友是否相等就可以

O(n)变O(1)!

为什么呢? 因为每个人的朋友圈都要去查看一遍,如果A的朋友圈没有B,但是B的朋友圈有A

那么,B和A的朋友圈肯定对不上,所以就输出NO了

•代码

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 const int maxn=2e5+5;
 5 vector<int> a[maxn];
 6 //我加vis数组是为了减少不必要的查看来减少时间
 7 //氮素 貌似效率还不如不加vis的,搞不懂 ???
 8 bool vis[maxn];
 9 
10 int main()
11 {
12 //    freopen("C:\\Users\\14685\\Desktop\\C++workspace\\in&out\\contest","r",stdin);
13     int n,m;
14     cin>>n>>m;
15     for(int i=1;i<=m;i++)
16     {
17         int x,y;
18         cin>>x>>y;
19         a[x].push_back(y);
20         a[y].push_back(x);
21     }
22     ///把自己也加进自己的朋友圈
23     for(int i=1;i<=n;i++)
24         a[i].push_back(i);
25     ///排序
26     for(int i=1;i<=n;i++)
27         sort(a[i].begin(),a[i].end());
28 
29     ///查看每个人的朋友圈
30     for(int i=1;i<=n;i++)
31     {
32         if(vis[i])
33             continue;
34         ///自己的朋友圈和朋友的朋友圈对比
35         for(int j=0;j<a[i].size();j++)
36         {
37             if(a[i].size()==a[a[i][j]].size())
38             {
39                 vis[a[i][j]]=true;
40                 ///朋友圈不相同
41                 if(a[i][0]!=a[a[i][j]][0])
42                 {
43                     puts("NO");
44                     return 0;
45                 }
46             }
47             else
48             {
49                  puts("NO");
50                  return 0;
51             }
52         }
53     }
54     puts("YES");
55 }
View Code

 

B - Bear and Different Names(模拟)

•题意

有n个人,从1个人开始每k个一组

[1,k] [2,k+1]....

如果有重名的则NO,否则YES

根据NO和YES的结果输出名字

名字首字母大写,且1<=名字长度<=10

•思路

因为名字长度有限制,可能有很多人名字不同

所以首先预处理出不同名字来,注意计算名字个数

(这里踩了坑)

首先找到第一个YES为切入点,后边的名字可以由他得到

已经确定了的名字不能再更改!否则会引起与前面的YES/NO不符 (这里也踩了坑

如果是YES的话就选择新名字,否则选择和这k个人的第一个人相同的名字(也就是首尾名字重合

然后再找第一个YES前面的人,从第一个YES开始倒找,让他前面的都和他重名

•代码

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 const int maxn=2e5+5;
 5 string name[55],nname[3000];
 6 string s[55];
 7 int wh[10];
 8 ///预处理名字
 9 void Init()
10 {
11     for(int i=0;i < 10;++i)
12         wh[i]=i;
13     int cnt=0;
14     do
15     {
16         for(int i=0;i<10;i++)
17         {
18             if(i==0)
19                 nname[++cnt]=wh[0]+'A';
20             else
21                 nname[cnt]+=(wh[i]+'a');
22         }
23         if(cnt>2500)
24             return ;
25     }while(next_permutation(wh,wh+10));
26 }
27 int main()
28 {
29 //    freopen("C:\\Users\\14685\\Desktop\\C++workspace\\in&out\\contest","r",stdin);
30     int n,m;
31     cin>>n>>m;
32     bool flag=false;
33     Init();
34     int cut=0;
35     for(int i=1;i<=n-m+1;i++)
36     {
37         cin>>s[i];
38         if(s[i]=="YES")
39         {
40             flag=true;
41             for(int j=i;j<=i+m-1;j++)
42                 if(name[j].empty())
43                     name[j]=nname[++cut];///选择新名字
44         }
45         else///这k个人首尾名字重合
46             name[i+m-1]=name[i];
47     }
48 
49     if(!flag)
50         for(int i=1;i<=n;i++)
51             cout<<"Aa"<<' ';
52     else
53     {
54         ///找第一个YES
55         int index;
56         for(index=1;index<=n-m+1;index++)
57             if(s[index]=="YES")
58                 break;
59         ///前面的人和他重名
60         for(int i=index;i>=0;i--)
61             name[i]=name[index];
62 
63         for(int i=1;i<=n;i++)
64             cout<<name[i]<<' ';
65     }
66 }
View Code

 

D . Bear and Company(带技巧的dp)

•题意

有一种操作可交换相邻两个字母

现给出一个S串,要求没有VK相连,求最小的操作数

•思路

在ljp的帮助下(tql...),终于AC了这个题

s串中包含三种字母,V K和其他字母(因为其他字母无论是什么对VK都不产生影响)

设dp数组$f[i][j][k][0/1]$

前$i$个V,$j$个K,$k$个其他字母结尾不是$V$/不是$V$的最小操作数

把V K和Other 分开

for(int i=0;i<n;i++)
{
if(s[i]=='V') v[0].push_back(i); else if(s[i]=='K') v[1].push_back(i); else v[2].push_back(i); }

首先看作一个空串,然后往后面添加字母

V可以接在V K Other后面

K可以接在K Other后面

Other可以接在V K Other后面

由于是接在后面的,那就要把本来应该在他后面但是现在在他前面的字母转移到后面

并且这些字母与他只会进行一次交换

例如ABCD中A转移到D后面,

ABCD->BACD->BCAD->BCDA

A只会和D进行一次交换

添加V使得V在末尾影响$f[i][j][k][1]$

添加K使得V不在末尾影响$f[i][j][k][0]$

添加Other使得V不在末尾影响$f[i][j][k][0]$

由于添加K和Other都是V不在末尾,如果两者都添加的话,一定要取最小值

f数组必须初始化,因为有依赖关系

•代码

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 ///v[0]:V  v[1]:K  v[2]:O
 4 vector<int> v[3];
 5 ///f[i][j][k][0/1]
 6 ///前i个V,j个K,k个O 尾位是否是V 的最小交换
 7 int f[80][80][80][2];
 8 int n;
 9 string s;
10 int main()
11 {
12     cin>>n>>s;
13     for(int i=0;i<n;i++)
14     {
15         if(s[i]=='V')
16             v[0].push_back(i);
17         else if(s[i]=='K')
18             v[1].push_back(i);
19         else
20             v[2].push_back(i);
21     }
22 
23     ///初始化
24     for(int i=0;i<=v[0].size();i++)
25         for(int j=0;j<=v[1].size();j++)
26             for(int k=0;k<=v[2].size();k++)
27                 f[i][j][k][0]=f[i][j][k][1]=0x3f3f3f3f;
28     f[0][0][0][0]=0;
29 
30     int cnt=0;
31     for(int i=0;i<=v[0].size();i++)
32     {
33         for(int j=0;j<=v[1].size();j++)
34         {
35             for(int k=0;k<=v[2].size();k++)
36             {
37                 if(i||j||k)
38                 {
39                     if(i)///0 V
40                     {
41                         ///V可以接在V K O后面
42                         cnt=min(f[i-1][j][k][0],f[i-1][j][k][1]);
43                         ///交换位置 K 1
44                         for(int m=0;m<j;m++)
45                             if(v[1][m]>v[0][i-1]) cnt++;
46                         ///交换位置 O 2
47                         for(int m=0;m<k;m++)
48                             if(v[2][m]>v[0][i-1]) cnt++;
49 
50                         f[i][j][k][1]=cnt;
51                     }
52                     if(j)///1 K
53                     {
54                         ///K可以接在K O后面
55                         cnt=f[i][j-1][k][0];
56                         ///交换位置 V 0
57                         for(int m=0;m<i;m++)
58                             if(v[0][m]>v[1][j-1]) cnt++;
59                         ///交换位置 O 2
60                         for(int m=0;m<k;m++)
61                             if(v[2][m]>v[1][j-1]) cnt++;
62 
63                         f[i][j][k][0]=cnt;
64                     }
65                     if(k)///2 O
66                     {
67                         ///O可以接在V K O后面
68                         cnt=min(f[i][j][k-1][0],f[i][j][k-1][1]);
69                         ///交换位置 V 0
70                         for(int m=0;m<i;m++)
71                             if(v[0][m]>v[2][k-1]) cnt++;
72                         ///交换位置 K 1
73                         for(int m=0;m<j;m++)
74                             if(v[1][m]>v[2][k-1]) cnt++;
75 
76                         f[i][j][k][0]=min(f[i][j][k][0],cnt);///取最小值
77                     }
78                 }
79             }
80         }
81     }
82 
83     int i=v[0].size(),j=v[1].size(),k=v[2].size();
84     cout<<min(f[i][j][k][0],f[i][j][k][1])<<endl;
85 }
View Code

 

转载于:https://www.cnblogs.com/MMMinoz/p/11561321.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值