Codeforces Round #486 (Div. 3)

988A.http://codeforces.com/contest/988/problem/A

题意:给出n个数,让你从中取出m个不同的数组成一组

分析:模拟即可。当每个人为第一次出现时,标记这个位置可取。最后从可取的位置取m个即可

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<vector>
 5 using namespace std;
 6 const int maxn=105;
 7 int a[maxn];
 8 bool vis[maxn],used[maxn];
 9 
10 int main()
11 {
12     int n,m,i,j,k,x,y,z,ans,cnt;
13     while ( scanf("%d%d",&n,&m)!=EOF )
14     {
15         memset(vis,false,sizeof(vis));
16         memset(used,false,sizeof(used));
17         ans=cnt=0;
18         for ( i=1;i<=n;i++ ) 
19         {
20             scanf("%d",&a[i]);
21             if ( !vis[a[i]] )
22             {
23                 vis[a[i]]=true;
24                 used[i]=true;
25                 ans++;
26             }
27         }
28         if ( m>ans ) printf("NO\n");
29         else 
30         {
31             printf("YES\n");
32             for ( i=1;i<=n;i++ )
33             {
34                 if ( used[i] )
35                 {
36                     printf("%d",i);
37                     if ( ++cnt!=m ) printf(" ");
38                     else printf("\n");
39                 }
40                 if ( cnt==m ) break;
41             }
42         }
43     }
44     return 0;
45 }
A

988B.http://codeforces.com/contest/988/problem/B

题意:给出n个字符串,让你对字符串进行排序,使得前一个字符串是后一个字符串的字串

分析:以字符串长度为第一关键字,字符串的字典序为第二关键字进行排序。利用KMP(暴力应该也行)判断前一串是否为后一串的子串

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<string>
 5 #include<iostream>
 6 using namespace std;
 7 const int maxn=110;
 8 string s[maxn];
 9 
10 int cmp(string a,string b)  
11 {  
12     if ( a.size()==b.size() ) return a<b;
13     return a.size()<b.size();  
14 }  
15 
16 int Next[maxn]; 
17 char x[maxn],y[maxn]; 
18 
19 void pre_kmp(int m)
20 {
21     int i,j;
22     j=Next[0]=-1;
23     i=0;
24     while ( i<m ) { 
25         while ( j!=-1 && x[i]!=x[j] ) j=Next[j]; 
26         Next[++i]=++j;
27     }    
28 } 
29 
30 bool kmp(int n,int m)
31 {
32     int i,j;
33     pre_kmp(m);
34     i=j=0;
35     while ( i<n ) {  
36         while ( j!=-1 && y[i]!=x[j] ) j=Next[j]; 
37         i++;j++; 
38         if ( j>=m ) return true;
39     }
40     return false;
41 }
42 
43 int main()
44 {
45     int n,i,j,k,z;
46     bool flag;
47     while ( scanf("%d",&n)!=EOF )
48     {
49         for ( i=1;i<=n;i++ ) cin>>s[i];
50         sort(s+1,s+1+n,cmp);
51         flag=true;
52         //for ( i=1;i<=n;i++ ) cout<<s[i]<<endl;
53         for ( i=1;i<n;i++ )
54         {
55              int lenx=s[i].size();
56              int leny=s[i+1].size();
57              for ( j=0;j<lenx;j++ ) x[j]=s[i][j];
58              for ( j=0;j<leny;j++ ) y[j]=s[i+1][j];
59              if ( !kmp(leny,lenx) ) 
60              {
61                  flag=false;
62                  break;
63             }
64         }
65         if ( !flag ) printf("NO\n");
66         else {
67             printf("YES\n");
68             for ( i=1;i<=n;i++ ) cout<<s[i]<<endl;
69         }
70     }
71     return 0;
72 }
B

988C.http://codeforces.com/contest/988/problem/C

题意:给定m个序列,第i个序列长度为ni。现在你可以从任意两个序列(不能是同一个)取出一个数,使得这两个序列剩下数字的和相等。求这两个序列的编号,和所取数的下标

分析:map<int,pair<int,int>>,first代表一个序列去掉一个数的和,second中的第一个代表是哪串序列,second中的第二个代表是该序列中哪个位置的数被取出。所以每次算出去掉一个数的和以后去找map中是否本来已经存在那个数即可

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<map>
 5 using namespace std;
 6 typedef pair<int,int>P;
 7 const int maxn=2e5+10;
 8 map<int,P>mp;
 9 int a[maxn];
10 
11 int main()
12 {
13     int k,n,m,i,j,x,y,z,sum;
14     P p1,p2;
15     bool flag;
16     while ( scanf("%d",&m)!=EOF )
17     {
18         mp.clear();
19         flag=false;
20         for ( i=1;i<=m;i++ )
21         {
22             scanf("%d",&n);
23             sum=0;
24             for ( j=1;j<=n;j++ ) 
25             {
26                 scanf("%d",&a[j]);
27                 sum+=a[j];
28             }
29             if ( flag ) continue;
30             for ( j=1;j<=n;j++ )
31             {
32                 k=sum-a[j];
33                 if ( mp.find(k)!=mp.end() )
34                 {
35                     if ( mp[k].first==i ) continue;
36                     else {
37                         p1=mp[k];
38                         p2=P(i,j);
39                         flag=true;
40                         break;
41                     }
42                 }
43                 else mp[k]=P(i,j);
44                 if ( flag ) break;
45             }
46         }
47         if ( !flag ) printf("NO\n");
48         else
49         {
50             printf("YES\n");
51             printf("%d %d\n",p1.first,p1.second);
52             printf("%d %d\n",p2.first,p2.second);
53         }
54     }
55     return 0;
56 }
C

988D.http://codeforces.com/contest/988/problem/D

题意:给出一串长度为n的序列。现要从中取一个元素子集,使得该子集中任意两个数差的绝对值是2的倍数(1也算2的倍数),求子集中的元素最多有多少个

分析:由分析可得元素个数出现的可能只能是1 2 3。1的情况是任意两个数的差都不为2的倍数。2的情况存在两个数的差是2的倍数。三的情况是存在三个数a,b,c(假设a<b<c),满足d=b-a=c-a=2^x,即两两之间的差相等且是2的倍数。所以先预处理出2的倍数有哪些,然后将出现的数存入set中,对于每个数a去先求得b和c然后去找b和c在不在set中

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<set>
 5 #include<cmath>
 6 using namespace std;
 7 typedef long long ll;
 8 const int maxn=2e5+10;
 9 ll a[maxn];
10 ll num[70];
11 set<ll>st;
12 
13 int main()
14 {
15     ll n,m,i,j,k,x,y,z,ans,u,v,w;
16     bool flag;
17     while ( scanf("%lld",&n)!=EOF )
18     {
19         st.clear();
20         ans=1;
21         for ( i=0;i<=63;i++ ) num[i]=(pow(2,i));
22         for ( i=1;i<=n;i++ ) 
23         {
24             scanf("%lld",&a[i]);
25             st.insert(a[i]);
26         } 
27         x=a[1];
28         for ( i=1;i<=n;i++ )
29         {
30             if ( ans==3 ) break;
31             m=a[i];
32             for ( j=0;j<=62;j++ )
33             {
34                 
35                 u=m+num[j];
36                 v=m+num[j+1];
37                 if ( st.find(u)!=st.end() )
38                 {
39                     ans=2;
40                     x=a[i];
41                     y=u;
42                 }
43                 if ( st.find(u)!=st.end() && st.find(v)!=st.end() )
44                 {
45                     ans=3;
46                     x=a[i];
47                     y=u;
48                     z=v;
49                     break;
50                 }
51             }
52         }
53         printf("%lld\n",ans);
54         if ( ans==1 ) printf("%lld\n",x);
55         else if ( ans==2 ) printf("%lld %lld\n",x,y);
56         else if ( ans==3 ) printf("%lld %lld %lld\n",x,y,z);
57     }
58     return 0;
59 }
D

988E.http://codeforces.com/contest/988/problem/E

题意:给出一个数n,现在可以交换相邻两个数,先求通过最少的交换次数使得得到的数是25的倍数(没有前导0),求最少的次数

分析:一个数是25的倍数那么这个是数的后两位一定是00,25,50,75这四种情况中的一种。那么我们只需要求出这四种情况所需要的次数,然后取最小值即可。

至于交换过程,我们需要让尽量右边的满足情况的数交换到相应的位置(贪心的思想)。同时注意不能出现前导0,如果第一个数是0的话,需要交换于从左到右第一个非0同时又不能是后两位的数进行交换

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<string>
 5 #include<iostream>
 6 using namespace std;
 7 const int inf=1e9;
 8 string t;
 9 int n;
10 
11 int cal(string nw)
12 {
13     string s=t;
14     int cnt=0;
15     for ( int i=n-1;i>=0;i-- )
16     {
17         if ( s[i]==nw[1] ) 
18         {
19             for ( int j=i;j+1<n;j++ )
20             {
21                 swap(s[j],s[j+1]);
22                 cnt++;
23             }
24             break;
25         }
26     }
27     if ( s[n-1]!=nw[1] ) return inf;
28     for ( int i=n-2;i>=0;i-- )
29     {
30         if ( s[i]==nw[0] ) 
31         {
32             for ( int j=i;j+2<n;j++ )
33             {
34                 swap(s[j],s[j+1]);
35                 cnt++;
36             }
37             break;
38         }
39     }
40     if ( s[n-2]!=nw[0] ) return inf;
41     if ( s[0]=='0' )
42     {
43         for ( int i=1;i<n-2;i++ )
44         {
45             if ( s[i]!='0' ) 
46             {
47                 for ( int j=i;j>0;j-- )
48                 {
49                     swap(s[j],s[j-1]);
50                     cnt++;
51                 }
52                 break;
53             }
54         }
55     }
56     if ( s[0]=='0' ) return inf;
57     else return cnt;
58 }
59 
60 
61 int main()
62 {
63     int i,j,k,ans;
64     while ( cin>>t )
65     {
66         n=t.size();
67         ans=inf;
68         ans=min(ans,cal("00"));
69         ans=min(ans,cal("25"));
70         ans=min(ans,cal("50"));
71         ans=min(ans,cal("75"));
72         if ( ans==inf ) printf("-1\n");
73         else printf("%d\n",ans);
74     }
75     return 0;
76 }
E

988F.http://codeforces.com/contest/988/problem/F

题意:一个人要从0走到a,其中有n段会下雨,有m个点有伞(在有雨的地方必须要有伞,可以同时拿多把伞,伞可以随时放下,也可以随时拿起,同时一个点可能有多把伞),每个伞都有重量w,拿着伞走x距离会增加x*w的疲惫值,求走到a点最小的疲惫值

分析:普通的dp题。

法1:dp[i][j]表示从0到i时,在点i有第j把伞(没有伞时j==0)时的最小疲惫值。

转移时,分成三种情况(即对应着三种操作)

dp[i+1][j]=min(dp[i+1][j],dp[i][j]+w[j])  (第i+1个点下雨,同时没带伞时不成立,其他条件都成立)  该操作是从第i个点到第i+1个点啥也不做,即第i+1个点的状态和第i个点的状态相同

dp[i+1][0]=min(dp[i+1][0],dp[i][j]) (当下一个点没下雨时成立) 该操作是在第i个点放下伞

dp[i+1][id[i]]=min(dp[i+1][id[i]],dp[i][j]+w[id[i]])  (在第i个点有伞) 该操作是第i个点有伞,拿起伞走到第i+1个点上去

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 typedef long long ll;
 6 typedef pair<int,int> P;
 7 const int maxn=2010;
 8 const int inf=1e9;
 9 int dp[maxn][maxn],id[maxn];
10 bool vis[maxn];
11 P umb[maxn];
12 
13 int main()
14 {
15     int a,n,m,i,j,k,x,y,z,ans,l,r;
16     while ( scanf("%d%d%d",&a,&n,&m)!=EOF )
17     {
18         memset(vis,false,sizeof(vis));
19         memset(id,0,sizeof(id));
20         for ( i=1;i<=n;i++ )
21         {
22             scanf("%d%d",&l,&r);
23             for ( j=l+1;j<=r;j++ ) vis[j]=true;
24         }
25         for ( i=1;i<=m;i++ ) scanf("%d%d",&umb[i].first,&umb[i].second);
26         sort(umb+1,umb+1+m);
27         for ( i=1;i<=m;i++ ) {
28             if ( id[umb[i].first]==0) id[umb[i].first] = i;
29         }
30         umb[0].second=0;
31         for ( i=0;i<=a;i++ )
32         {
33             for ( j=0;j<=m;j++ ) dp[i][j]=inf;
34         }
35         dp[0][0]=0;
36         for ( i=0;i<a;i++ )
37         {
38             for ( j=0;j<=m;j++ )
39             {
40                 if ( j!=0 || !vis[i+1] ) dp[i+1][j]=min(dp[i+1][j],dp[i][j]+umb[j].second);
41                 if ( !vis[i+1] ) dp[i+1][0]=min(dp[i+1][0],dp[i][j]);
42                 if ( id[i] ) dp[i+1][id[i]]=min(dp[i+1][id[i]],dp[i][j]+umb[id[i]].second);
43             }
44         }
45         ans=inf;
46         for ( i=0;i<=m;i++ ) ans=min(dp[a][i],ans);
47         if ( ans!=inf ) printf("%d\n",ans);
48         else printf("-1\n");
49     }
50     return 0;
51 }
F(法1)

法2:dp[i]代表从0到i时最小的疲惫值.

转移时分为两种情况:

dp[i+1]=min(dp[i+1],dp[i])   (第i+1个点没下雨)

dp[j]=min(dp[j]+dp[i]+(j-i)*x)  (第i个点有伞) (i+1<=j<=a,x代表在第i个点的伞的重量)  该转移代表的含义是,拿着这把伞后从i点能到哪个位置(j)

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 const int maxn=2010;
 6 const int inf=1e9;
 7 int b[maxn],w[maxn],dp[maxn],mas[maxn];
 8 bool vis[maxn];
 9 
10 int main()
11 {
12     int a,n,m,i,j,k,x,y,z,ans,l,r;
13     while ( scanf("%d%d%d",&a,&n,&m)!=EOF )
14     {
15         memset(vis,false,sizeof(vis));
16         for ( i=1;i<=n;i++ )
17         {
18             scanf("%d%d",&l,&r);
19             for ( j=l+1;j<=r;j++ ) vis[j]=true;
20         }
21         for ( i=0;i<=a;i++ ) dp[i]=mas[i]=inf;
22         for ( i=1;i<=m;i++ )
23         {
24             scanf("%d%d",&b[i],&w[i]);
25             mas[b[i]]=min(mas[b[i]],w[i]);
26         }
27         dp[0]=0;
28         for ( i=0;i<a;i++ )
29         {
30             if ( dp[i]==inf ) continue;
31             if ( !vis[i+1] ) dp[i+1]=min(dp[i+1],dp[i]);
32             if ( mas[i]!=inf )
33             {
34                 for ( j=i+1;j<=a;j++ ) dp[j]=min(dp[j],dp[i]+(j-i)*mas[i]);
35             }
36         }
37         if ( dp[a]!=inf ) printf("%d\n",dp[a]);
38         else printf("-1\n");
39     }
40     return 0;
41 }
F(法2)

猜你喜欢

转载自www.cnblogs.com/HDUjackyan/p/9125241.html
今日推荐