noip模拟赛4总结

#(补笔记)

#坑中第二期集训day11

今天没有什么心情写总结/晕

因为考试又是一如既往地被dalao们碾压了(可能这就是蒟蒻吧)

好吧还是要(很不情愿地)回顾一下的

嗯那就直接上题目吧

T1 巧克力

emmm这题本来还在感到特别费解不能搜索也不会dp

不过在一番很久的思考后发现n很小,好像可以模拟做……

模拟的过程:

1、从第一位开始逐个找一段连续的区间(len≥1)

2、将区间向两个方向拓展,很容易发现若某字母想从第A位移到第B位,交换次数一定为|A-B|

3、储存下向左和向右拓展一定个相同字母所需的交换次数,用fl[i],fr[i]记录向左右拓展i个相同字母所需的交换次数,则:

  

那就直接上代码咯~

 1 #include <iostream>
 2 #include <cstdio>
 3 
 4 using namespace std;
 5 
 6 int n, sw, ans;
 7 char s[55];
 8 int fl[55], fr[55];
 9 
10 int main()
11 {
12     freopen("a.in", "r", stdin);
13     freopen("a.out", "w", stdout);
14     int T;
15     cin >> T;
16     while (T--)
17     {
18         scanf("%d%d", &n, &sw);
19         scanf("%s", s);
20         ans = 1;
21         for (int i = 0; i < n; i++)                    // 枚举每个区间的左端点 
22         {
23             if (i == 0 || s[i - 1] != s[i])            // 确定该点是左端点 
24             {
25                 int j, len, cntl, cntr, rpos;
26                 cntl = 0;
27                 for (j = i - 1; j > 0; j--)            // 向左枚举 
28                 {
29                     if (fl[cntl] + (i - cntl) - j - 1 > sw) break;
30                     if (s[j] == s[i])
31                     {
32                         fl[cntl + 1] = fl[cntl] + (i - cntl) - j - 1;
33                         cntl++;
34                     }
35                 }
36                 cntr = 0;
37                 while (s[i + cntr + 1] == s[i + cntr]) fr[++cntr] = 0;    // 找到区间右端点 
38                 for (j = i + cntr + 1; j < n; j++)    // 向右枚举 
39                 {
40                     if (fr[cntr] + j - (i + cntr) - 1 > sw) break;
41                     if (s[j] == s[i])
42                     {
43                         fr[cntr + 1] = fr[cntr] + j - (i + cntr) - 1;
44                         cntr++;
45                     }
46                 }
47                 for (int k = 0; k <= cntl; k++)
48                     for (int l = 0; l <= cntr; l++)
49                         if (fl[k] + fr[l] <= sw)
50                             ans = max(ans, k + l + 1);
51             }
52         }
53         printf("%d\n", ans);
54     }
55     return 0;
56 }

T2 最小逆序对

这题应该是看起来最水的一题啦~

然后我还是错了希望教练原谅我这种没有重新初始化的智障

观察数据范围:

应该可以想到算法的时间复杂度应该低于O(nlogn),而通过树状数组/线段树或归并求逆序对的时间复杂度是O(nlogn),因此应该先进行预处理求出原数组中的逆序对个数,再对每一个数进行处理

对于样例,用cnt[i]表示以第i个数为结尾的逆序对个数,sum表示每一个序列中的逆序对总数

(emmm我是把一个数从后面移到前面的,感觉这样可能会好理解一点?)

可以看出,除了每一次移动的数字x,有且只有比移动的数字大的数字,cnt值会改变,且都是增加1(逆序对{x,1})

因此,在最后一个数是a[i],此时的sum值为:

  

代码:

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 
 5 #define MAXN 5005
 6 
 7 using namespace std;
 8 
 9 int n, ans;
10 int a[MAXN], c[MAXN], f[MAXN];
11 int sum[MAXN];
12 
13 int lowbit(int x)                    // 树状数组求逆序对数进行预处理 
14 {
15     return x & (-x);
16 }
17 void add(int pos, int x)
18 {
19     while (pos <= n)
20     {
21         c[pos] += x;
22         pos += lowbit(pos);
23     }
24 }
25 long long query(int pos)            // 区间修改,单点查询 
26 {
27     long long res = 0;
28     while (pos > 0)
29     {
30         res += c[pos];
31         pos -= lowbit(pos);
32     }
33     return res;
34 }
35 
36 int main()
37 {
38 //    freopen("b.in", "r", stdin);
39 //    freopen("b.out", "w", stdout);
40     while (cin >> n)
41     {
42         memset(c, 0, sizeof(c));
43         memset(sum, 0, sizeof(sum));
44         for (int i = 1; i <= n; i++)
45         {
46             scanf("%d", &a[i]);
47             a[i]++;
48             f[i] = query(a[i]);
49             sum[n] += f[i];
50             add(1, 1);
51             add(a[i], -1);
52         }                                    // 读入时进行预处理,时间复杂度O(nlogn) 
53         ans = sum[n];
54         for (int i = n - 1; i >= 1; i--)    // 枚举数组的最后一个数的原下标 
55         {
56             sum[i] = sum[i + 1] - (n - a[i + 1]) + a[i + 1] - 1;
57             ans = min(ans, sum[i]);
58         }
59         printf("%d\n", ans);
60     }
61 }

T3  悟空

 

今天的最后一题~

做到这一题的时候就只剩40分钟了,想不到正解555

然后就打了暴力dfs加上很简陋的剪枝

算法的时空复杂度好像都是O(n!),想着应该凉了

然后数据太水……老师说数据是随机出的……没有想过专门搞一个完全图来卡我……然后就过了

先贴一下蒟蒻的垃圾算法吧

emmm我默认spfa和dfs不用解释了嗷

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <queue>
  5 
  6 #define MAXN 305
  7 
  8 using namespace std;
  9 
 10 int n, m, ans;
 11 int start1, end1, start2, end2;
 12 int dist1, dist2;
 13 
 14 struct {
 15     int v, w, next;
 16 } edge[MAXN * MAXN];
 17 int cnt;
 18 int head[MAXN];
 19 bool added[MAXN][MAXN];
 20 
 21 void add_edge(int a, int b, int c)
 22 {
 23     if (added[a][b])
 24         return;
 25     added[a][b] = 1;
 26     cnt++;
 27     edge[cnt].v = b;
 28     edge[cnt].w = c;
 29     edge[cnt].next = head[a];
 30     head[a] = cnt;
 31 }
 32 
 33 
 34 int SPFA(int start, int end)            // SPFA预处理用于求最短路长度,用于dfs剪枝 
 35 {
 36     int dist[MAXN];
 37     bool vis[MAXN];
 38     queue<int> q;
 39     q.push(start);
 40     memset(vis, 0, sizeof(vis));
 41     vis[start] = 1;
 42     memset(dist, 0x7f, sizeof(dist));
 43     dist[start] = 0;
 44     while (!q.empty())
 45     {
 46         int now = q.front();
 47         q.pop();
 48         vis[now] = 0;
 49         for (int i = head[now]; i; i = edge[i].next)
 50         {
 51             int v = edge[i].v;
 52             if (dist[now] + edge[i].w < dist[v])
 53             {
 54                 dist[v] = dist[now] + edge[i].w;
 55                 if (!vis[v])
 56                 {
 57                     q.push(v);
 58                     vis[v] = 1;
 59                 }
 60             }
 61         }
 62     }
 63     return dist[end];
 64 }                                        // 特别标准的spfa了吧…… 
 65 
 66 
 67 vector<bool> book; 
 68 vector<vector<bool> > v1, v2;            // 储存所有的最短路径,不知道有多少只能开vector咯 
 69 
 70 void dfs1(int sum, int now)
 71 {
 72     if (sum > dist1)                    // 很烂的剪枝…… 
 73         return;
 74     if (now == end1)                    // 直接把book数组全存进去 
 75         v1.push_back(book);
 76     else {
 77         for (int i = head[now]; i; i = edge[i].next)
 78         {
 79             int v = edge[i].v;
 80             if (book[v]) continue;
 81             book[v] = 1;
 82             dfs1(sum + edge[i].w, v);
 83             book[v] = 0;
 84         }
 85     }
 86 }
 87 void dfs2(int sum, int now)                // 另一条最短路 
 88 {
 89     if (sum > dist2)
 90         return;
 91     if (now == end2)
 92         v2.push_back(book);
 93     else {
 94         for (int i = head[now]; i; i = edge[i].next)
 95         {
 96             int v = edge[i].v;
 97             if (book[v]) continue;
 98             book[v] = 1;
 99             dfs2(sum + edge[i].w, v);
100             book[v] = 0;
101         }
102     }
103 }
104 
105 int main()
106 {
107     while (true)
108     {
109         scanf("%d%d", &n, &m);
110         if (n == 0 && m == 0)                // 数据结束标志 
111             return 0;
112         cnt = 0;                            // 注意重新初始化!!! 
113         ans = 0;
114         memset(head, 0, sizeof(head));
115         memset(added, 0, sizeof(added));
116         for (int i = 1; i <= m; i++)
117         {
118             int a, b, c;
119             scanf("%d%d%d", &a, &b, &c);
120             add_edge(a, b, c);
121             add_edge(b, a, c);                // 双向边 
122         }
123         scanf("%d%d%d%d", &start1, &end1, &start2, &end2);
124         dist1 = SPFA(start1, end1);
125         dist2 = SPFA(start2, end2);
126         v1.clear();
127         v2.clear();
128         book.clear();                        // 初始化#2 
129         for (int i = 0; i <= n; i++) book.push_back(0);
130         book[start1] = 1;
131         dfs1(0, start1);
132         book.clear(); 
133         for (int i = 0; i <= n; i++) book.push_back(0);
134         book[start2] = 1;
135         dfs2(0, start2);
136         for (int i = 0; i < v1.size(); i++)
137             for (int j = 0; j < v2.size(); j++)        //直接枚举所有最短路…… 
138             {
139                 int tot = 0;
140                 for (int k = 1; k <= n; k++)
141                     if (v1[i][k] && v2[j][k])
142                         tot++;
143                 ans = max(tot, ans);
144             }
145         cout << ans << endl;
146     }
147 }

咳不用吐槽我的暴力了

我们还是来寻找正解吧2333

根据最短路径的定义:

可以发现,如果一条最短路径上经过两点a,b,则一定会经过他们之间的最短路{mindist(a,b)}

因此,如果悟空和唐僧的最短路同时有两个点a,b,则{mindist(a,b)}中的某一条,经过的点数最多的最短路,一定在他们所走的路上,只有这样才能保证路径最短的情况时,公共点最多

即:在两人选择的路径上的顶点按经过顺序构成的区间中,有不多于一段公共的连续区间(相同点的顺序可能相反,即有可能悟空经过1->2->3->5->4,而唐僧经过6->5->3->2->7,则此时{2,3,5}为公共点集

那么这题就可以很好地转换为一个区间dp,通过floyd的方式求解

用dis[i][j]表示i->j的最短路,dp[i][j]表示最短路,则:

那就放一下老师的代码吧:

 1 #include <cstdio>
 2 #include <cstring>
 3 #define max(x,y) (x > y ? x : y)
 4 #define MAX 302
 5 #define INF 1000000000
 6 using namespace std;
 7 
 8 int dis[MAX][MAX],dp[MAX][MAX];
 9 int n,m;
10 
11 void floyd(){
12     for(int u=1;u<=n;u++){
13         for(int i=1;i<=n;i++){
14             for(int j=1;j<=n;j++){
15                 if(dis[i][j]>dis[i][u]+dis[u][j]){                // 可以更新最短路,必须要更新dp 
16                     dis[i][j]=dis[i][u]+dis[u][j];
17                     dp[i][j]=dp[i][u]+dp[u][j];
18                 }else if(dis[i][j]==dis[i][u]+dis[u][j]){        // 路径长度相同,取dp值较大的 
19                     dp[i][j]=max(dp[i][u]+dp[u][j],dp[i][j]);
20                 }
21             }
22         }
23     }
24 }
25 
26 int main()
27 {
28     int a,b,l;
29     int s1,e1,s2,e2;
30     int ans;
31     while(scanf("%d %d",&n,&m)==2 && (n+m)){
32         for(int i=1;i<=n;i++){
33             for(int j=1;j<=n;j++){
34                 dis[i][j]= i==j ? 0 : INF;
35                 dp[i][j]=0;
36             }
37         }
38         for(int i=0;i<m;i++){
39             scanf("%d %d %d",&a,&b,&l);
40             if(dis[a][b]>l){
41                 dis[a][b]=dis[b][a]=l;
42                 dp[a][b]=dp[b][a]=1;
43             }
44         }
45 
46         scanf("%d %d %d %d",&s1,&e1,&s2,&e2);
47         floyd();
48         ans=-1;
49         for(int i=1;i<=n;i++){
50             for(int j=1;j<=n;j++){
51                 if(dp[i][j]>ans && (dis[s1][e1] == dis[s1][i]+dis[i][j]+dis[j][e1]    // 检查i->j是否在两人的最短路上 
52                 ||dis[s1][e1] == dis[s1][j]+dis[j][i]+dis[i][e1])
53                 && (dis[s2][e2] == dis[s2][i]+dis[i][j]+dis[j][e2]
54                 ||dis[s2][e2] == dis[s2][j]+dis[j][i]+dis[i][e2]) ){
55                     ans=dp[i][j];
56                 }
57             }
58         }
59         printf("%d\n",ans+1);
60     }
61     return 0;
62 }

(前排吐槽一下老师的码风,484只有我一个人喜欢疯狂空格换行……)

今日份的总结大概就是这样啦~

还是暴露了自己相当多的不足吧

加油!

猜你喜欢

转载自www.cnblogs.com/Conless/p/11346637.html