Codeforces Round #302 (Div. 2)

A. Set of Strings

  题意:能否把一个字符串划分为n段,且每段第一个字母都不相同?

  思路:判断字符串中出现的字符种数,然后划分即可。

 1 #include<iostream>
 2 #include<set>
 3 #include<cstdio>
 4 #include<cstring>
 5 using namespace std;
 6 char s[110];
 7 set<char>st;
 8 int main()
 9 {
10     int n;
11     scanf("%d%s", &n,s);
12     int len = strlen(s);
13     for (int i = 0; i < len; i++)
14     {
15         if (!st.count(s[i]))st.insert(s[i]);
16     }
17     if (st.size() < n) printf("NO\n");
18     else
19     {
20         st.clear();
21         printf("YES\n");
22         int cur = 0;
23         while (n--)
24         {
25             st.insert(s[cur]);
26             while (cur<len&&st.count(s[cur]))
27             {
28                 printf("%c", s[cur++]);
29             }
30             if (n == 0)
31             {
32                 while(cur<len) printf("%c", s[cur++]); 
33             }
34             printf("\n");
35         }
36     }
37     return 0;
38 }
View Code

B. Sea and Islands

  题意:n*n的网格里都是水,现在需要填沙造出k个陆地,两两之间有边相邻的看成一片陆地。

  思路:讨论奇偶。

 1 #include<iostream>
 2 #include<cstdio>
 3 using namespace std;
 4 int main()
 5 {
 6     int n, k;
 7     scanf("%d%d", &n, &k);
 8     int maxs = n / 2 * n + n % 2 * (n + 1) / 2;
 9     if (k > maxs)printf("NO\n");
10     else
11     {
12         printf("YES\n");
13         for (int i = 0; i < n; i++)
14         {
15             int st;
16             if (i % 2==0) st = 0;
17             else st = 1;
18             for (int j=0; j < n; j++)
19             {
20                 if ((j - st) % 2 == 0&&k>0) printf("L"),k--;
21                 else printf("S");
22             }
23             printf("\n");
24         }
25     }
26     return 0;
27 }
View Code

C. Writing Code

  题意:有n个程序员,现在需要合作完成m行的代码,要求最多只有b个bug,求总方案数?

  思路:dp[i][j][k]:表示前i个程序员一起写j行代码bug数为k的方案数.采用滚动数组优化。

 1 #include<iostream>
 2 #include<cstdio>
 3 using namespace std;
 4 const int maxn = 510;
 5 const int maxm = 510;
 6 const int maxb = 510;
 7 int dp[2][maxm][maxb];//[i][j][k]表示前i个程序员一起写j行代码bug数为k的方案数
 8 int bugs[maxn];
 9 int main()
10 {
11     int n, m, b, mod;
12     scanf("%d%d%d%d", &n, &m, &b, &mod);
13     for (int i = 1; i <= n; i++) scanf("%d", bugs + i);
14     dp[0][0][0] = dp[1][0][0] = 1;
15     int pre, now;
16     for (int i = 1; i <= n; i++)
17     {
18         for (int j = 1; j <= m; j++)
19         {
20             for (int k =0; k <= b; k++)
21             {
22                 pre = ((i - 1) & 1),now=(i&1);
23                 if (k >= bugs[i]) dp[now][j][k] = (dp[pre][j][k] + dp[now][j - 1][k - bugs[i]]) % mod;
24                 else dp[now][j][k] = dp[pre][j][k];
25             }
26         }
27     }
28     int ans = 0;
29     for (int i = 0; i <= b; i++) ans = (ans + dp[now][m][i]) % mod;
30     printf("%d\n",ans);
31     return 0;
32 }
View Code

 D. Destroying Roads

  题意:有n个点,m条无向边。在保证s1到t1不超过l1小时、s2到t2不超过l2小时(每走一条边花费1小时)的情况下,求最多可以删去多少条边?

  思路:求出每两点之间的最短距离,然后2层循环枚举重复的路径。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<queue>
 5 #include<cstring>
 6 using namespace std;
 7 const int maxn = 3010;
 8 struct edge
 9 {
10     int next, to;
11     edge(int tt=0,int nn=0):to(tt),next(nn){}
12 };
13 edge Edge[maxn*maxn];
14 int Head[maxn], totedge;
15 void addedge(int from, int to)
16 {
17     Edge[totedge] = edge(to, Head[from]);
18     Head[from] = totedge++;
19     Edge[totedge] = edge(from, Head[to]);
20     Head[to] = totedge++;
21 }
22 int dis[maxn][maxn];
23 bool vis[maxn];
24 const int INF = 0x3f3f3f3f;
25 int n, m;
26 void SPFA(int st)
27 {
28     for (int i = 0; i <= n; i++) dis[st][i] = INF;
29     dis[st][st] = 0;
30     vis[st] = true;
31     queue<int>q;
32     q.push(st);
33     while (!q.empty())
34     {
35         int u = q.front();
36         q.pop();
37         vis[u] = false;
38         for (int i = Head[u]; i != -1; i = Edge[i].next)
39         {
40             int v = Edge[i].to;
41             if (dis[st][v] > dis[st][u] + 1)
42             {
43                 dis[st][v] = dis[st][u] + 1;
44                 if (!vis[v])
45                 {
46                     vis[v] = true;
47                     q.push(v);
48                 }
49             }
50         }
51     }
52 }
53 int main()
54 {
55     scanf("%d%d", &n, &m);
56     memset(Head, -1, sizeof(Head));
57     totedge = 0;
58     for (int i = 1; i <= m; i++)
59     {
60         int u, v;
61         scanf("%d%d", &u, &v);
62         addedge(u, v);
63     }
64     int s1, d1, l1, s2, d2, l2;
65     scanf("%d%d%d%d%d%d", &s1, &d1, &l1, &s2, &d2, &l2);
66     for (int i = 1; i <= n; i++) SPFA(i);
67     if (dis[s1][d1] > l1 || dis[s2][d2] > l2) printf("-1\n");
68     else
69     {
70         int minnum = dis[s1][d1] + dis[s2][d2];
71         for (int i = 1; i <= n; i++)
72         {
73             for (int j = 1; j <= i; j++)
74             {
75                 if (dis[s1][i] + dis[i][j] + dis[j][d1] <= l1 && dis[s2][i] + dis[i][j] + dis[j][d2] <= l2)
76                 {
77                     minnum = min(minnum, dis[s1][i] + dis[i][j] + dis[j][d1] + dis[s2][i] + dis[j][d2]);
78                 }
79                 if (dis[s1][i] + dis[i][j] + dis[j][d1] <= l1 && dis[s2][j] + dis[j][i] + dis[i][d2] <= l2)
80                 {
81                     minnum = min(minnum, dis[s1][i] + dis[i][j] + dis[j][d1] + dis[s2][j] + dis[i][d2]);
82                 }
83                 if (dis[s1][j] + dis[j][i] + dis[i][d1] <= l1 && dis[s2][i] + dis[i][j] + dis[j][d2] <= l2)
84                 {
85                     minnum = min(minnum, dis[s1][j] + dis[j][i] + dis[i][d1] + dis[s2][i] + dis[j][d2]);
86                 }
87                 if (dis[s1][j] + dis[j][i] + dis[i][d1] <= l1 && dis[s2][j] + dis[j][i] + dis[i][d2] <= l2)
88                 {
89                     minnum = min(minnum, dis[s1][j] + dis[j][i] + dis[i][d1] + dis[s2][j] + dis[i][d2]);
90                 }
91             }
92         }
93         printf("%d\n", m - minnum);
94     }
95     return 0;
96 }
View Code

 E. Remembering Strings

  题意:有n个字符串,每个字符串m位,要使得每个字符串存在一个位置i上的字符唯一(其他字符串该位上的字符与该字符串不同,称为易记字符串),你可以将某个字符串某位替换成任意字符,给出每个字符串每位替换的代价,求最小代价?

  思路:将n个字符串各自是否为易记标记为0/1,则我们可以进行状态压缩,对每个当前的状态,找到最低位的0(表示该字符串进行转换,其余高位的0无需转换),按照以下策略进行替换:如果该位唯一,则dp[i | (1 << j)] = dp[i],否则考虑2种方案:第一种方案,直接让该位字符唯一——dp[i | (1 << j)] = min(dp[i | (1 << j)], dp[i] + cost[j][k]);第二种方案,找到所有和该字符串该位字符一样的所有字符串,除去代价最大的,其他字符串进行转换——dp[i | tmp] = min(dp[i | tmp], dp[i] + sumc - maxc)。

  初始化为-1时需要考虑当前状态在此前是否已被转换到,没有则跳过;初始化为INF则不用考虑。

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<cstdio>
 4 #include<cstring>
 5 using namespace std;
 6 char str[25][25];
 7 int cost[25][25];
 8 const int maxc = 1 << 20;//最大状态数
 9 int dp[maxc];
10 int main()
11 {
12     int n, m;
13     scanf("%d%d", &n, &m);
14     for (int i = 0; i < n; i++) scanf("%s", str + i);
15     for (int i = 0; i < n; i++)
16     {
17         for (int j = 0; j < m; j++) scanf("%d", &cost[i][j]);
18     }
19     int total = (1 << n) - 1;//最终状态为111...11,n个1表示n个字符串都为易记
20     memset(dp, -1, sizeof(dp));
21     dp[0] = 0;
22     for (int i = 0; i <total; i++)//枚举状态
23     {
24         if (dp[i] == -1) continue;//该状态没有被转移到,跳过
25         int j = 0;
26         while (((i >> j) & 1) == 1) j++;//找到该状态中为非易记的字符串进行状态转移
27         for (int k = 0; k < m; k++)
28         {//让该字符串第k位的字符唯一
29             int cnt = 0, sumc = 0, maxc = 0, tmp = 0;
30             for (int z = 0; z < n; z++)
31             {
32                 if (str[z][k] == str[j][k])
33                 {
34                     cnt++;
35                     sumc += cost[z][k];//总代价
36                     maxc = max(maxc, cost[z][k]);//最大代价
37                     tmp |= (1 << z);//所有和该字符串该位一样的字符串为易记时的状态
38                 }
39             }
40             if (cnt == 1)//如果该位字符本就唯一
41             {
42                 if (dp[i | (1 << j)] == -1)dp[i | (1 << j)] = dp[i];
43                 else dp[i | (1 << j)] = min(dp[i | (1 << j)], dp[i]);
44             }
45             else//否则
46             {
47                 //第一种方案,直接让该位字符唯一
48                 if (dp[i | (1 << j)] == -1) dp[i | (1 << j)] = dp[i] + cost[j][k];
49                 else dp[i | (1 << j)] = min(dp[i | (1 << j)], dp[i] + cost[j][k]);
50                 //第二种方案,找到所有和该字符串该位字符一样的所有字符串,除去代价最大的,其他字符串进行转换
51                 if (dp[i | tmp] == -1) dp[i | tmp] = dp[i] + sumc - maxc;
52                 else dp[i | tmp] = min(dp[i | tmp], dp[i] + sumc - maxc);
53             }
54         }
55     }
56     printf("%d\n", dp[total]);
57     return 0;
58 }
View Code

 

posted @ 2018-04-06 14:21  萌萌的美男子  阅读(119)  评论(0编辑  收藏  举报