多校联合比赛部分题目

题目:http://acm.hdu.edu.cn/showproblem.php?pid=4308

题意:王子找公主。在一个图里面,如果是 * 则表示可以走,而且没走一个 * 需要的话费是 c 元,如果是 # 则不能通过,如果是 P 则可以跳到其他任意一个 P 所在的点,王子是 Y 表示,公主是 C 表示,问王子找到公主的最小花费。

思路:bfs,跟普通的bfs一样,但是由于题目里面说 P 与 P 是可以任意到达的(而且不需要任何花费),所以要特殊处理,用一个队保存可行点,从当前点开始搜索,如果遇到 * 则进队继续搜索,如果遇到 P 则要把图里面所有的 P 都搜出来进队,然后继续搜索。因为题目说 r * c <= 5000 ,怕超内存,这里我把二维的数组改成了一维的,下标进行了转换。不过貌似看网上有人也用的二维的

View Code
  1 #include <iostream>
  2 #include <string.h>
  3 #include <algorithm>
  4 #include <stack>
  5 #include <queue>
  6 #include <math.h>
  7 #include <stdlib.h>
  8 #include <stdio.h>
  9 #define N 5005
 10 #define inf 100000000
 11 #define _clr(a,val) (memset(a,val,sizeof(a)))
 12 
 13 using namespace std;
 14 
 15 struct node
 16 {
 17     int x;
 18     int y;
 19     int step;
 20 };
 21 int move[4][2] = {{1,0},{-1,0},{0,1},{0,-1}};
 22 node s,e;
 23 int n,m,c;
 24 int flag;
 25 int sum1,sum2,sum;
 26 char str[N];
 27 int v[N];
 28 int juge(int x,int y)
 29 {
 30     if(x < n && x >= 0 && y < m && y >= 0 && str[x * m + y] != '#' && !v[x * m + y])
 31     return 1;
 32     else return 0;
 33 }
 34 int bfs(node tt)
 35 {
 36     queue<node>qu;
 37     node t,k,tem;
 38     int i;
 39     qu.push(tt);
 40     while(!qu.empty())
 41     {
 42         t = qu.front();
 43         qu.pop();
 44         for(i = 0;i < 4; i++)
 45         {
 46             k.x = t.x + move[i][0];
 47             k.y = t.y + move[i][1];
 48             k.step = t.step;
 49             if(juge(k.x,k.y))
 50             {
 51                 v[k.x * m + k.y] = 1;
 52                 if(str[k.x * m + k.y] == 'C')
 53                 {
 54                     sum = k.step; break;
 55                 }
 56                 else if(str[k.x * m + k.y] == '*')
 57                 {
 58                     tem.x = k.x, tem.y = k.y, tem.step = k.step + 1;
 59                     qu.push(tem);
 60                 }
 61                 else if(str[k.x * m + k.y] == 'P') //如果是p则找到所有的p
 62                 {
 63                     for(i = 0;i < n * m; i++)
 64                     if(str[i] == 'P' && !v[i])
 65                     {
 66                         tem.x = i / m, tem.y = i % m, tem.step = k.step;
 67                         v[i] = 1;
 68                         qu.push(tem);
 69                     }
 70                 }
 71             }
 72         }
 73     }
 74     return sum;
 75 }
 76 int main()
 77 {
 78     int i,j;
 79     char ch;
 80     //freopen("data.txt","r",stdin);
 81     while(cin>>n>>m>>c)
 82     {
 83         //cout<<"n = "<<n * m<<endl;
 84         for(i = 0;i < n * m; i++)
 85         {
 86             cin>>str[i];
 87             //cout<<str[i]<<" ";
 88             v[i] = 0;
 89             {
 90                 if(str[i] == 'Y')
 91                 {
 92                     s.x = i / m, s.y = i % m, s.step = 0;
 93                 }
 94             }
 95         }
 96         //cout<<"s = "<<s.x<<" "<<s.y<<endl;
 97         sum = inf;
 98         v[s.x * m + s.y] = 1;
 99         flag = 0;
100         bfs(s);
101         //cout<<"sum = "<<sum<<endl;
102         if(sum == inf) {cout<<"Damn teoy!\n";continue;}
103         else cout<<sum * c <<endl;
104     }
105     return 0;
106 }

题目http://acm.hdu.edu.cn/showproblem.php?pid=4310

题意:类似打怪的游戏,每个英雄有一定的 dpsi 和 hp值,你每次选择攻击的那个英雄会掉一滴血,但是你的 dpsi 值会下降,下降的值是 你所打的英雄的 dpsi + 没有死的那些英雄的dpsi值,问如何选择英雄的顺序,可以使你的dpsi减少最少

思路:贪心,按dpsi / hp 从大到小排序,然后求和计算。官方解题说是状态dp,状态压缩dp,用dp[mask]表示杀死mask集合的敌人时,这些敌人造成的最小hp消耗。有转移方程dp[mask] = min{dp[mask - {i}] + hp_sum[mask] * dps[i], for all i in mask},我只能说,表示对dp不太知道,所以只能贴过来了

View Code
 1 #include <iostream>
 2 #include <string.h>
 3 #include <stdio.h>
 4 #include <algorithm>
 5 #define N 22
 6 #define _clr(a,val) (memset(a,val,sizeof(a)))
 7 
 8 using namespace std;
 9 
10 typedef long long ll;
11 struct node
12 {
13     int hp;
14     int dp;
15     double div;
16 }hero[N];
17 ll sum[N + 1];
18 int cmp(node a,node b)
19 {
20     return a.div > b.div;
21 }
22 int main()
23 {
24     int i,j;
25     int n;
26     //freopen("data.txt","r",stdin);
27     while(scanf("%d",&n) != EOF)
28     {
29         for(i = 1; i <= n; i++)
30         {
31             scanf("%d%d",&hero[i].dp,&hero[i].hp);
32             hero[i].div = (hero[i].dp * 1.0) / (hero[i].hp * 1.0);
33         }
34         if(n == 1) {cout<<hero[1].dp * hero[1].hp<<endl;continue;}
35         sort(hero + 1, hero + n + 1,cmp);
36         _clr(sum,0);
37         for(i = n; i >= 1; i--)
38         sum[i] = sum[i + 1] + hero[i].dp;
39         ll ans = 0;
40         for(i = 1; i <= n; i++)
41         ans += (ll)((hero[i].hp * sum[i]));
42         cout<<ans<<endl;
43     }
44     return 0;
45 }

题目:http://acm.hdu.edu.cn/showproblem.php?pid=4311

题意:给你n个点的坐标,找出其中一个点,使得其余n - 1个点,到这个点的距离最小,这里距离定义是曼哈顿距离 dis = abs(x1 - x2) + abs(y1 - y2);

思路:贴一下官方解题报告吧:

平面上两点间的 Manhattan 距离为 |x1-x2| + |y1-y2|

X 方向的距离与 Y 方向上的距离可以分开来处理。假设我们以 (xi,yi) 作为开会的地点,那么其余的点到该开会地点所需的时间为 X 方向上到 xi 所需要的时间加上 Y 方向上到 yi 所需要的时间。

对数据预处理后可以快速地求出x坐标小于xi的点的个数rankx, 并且这些 x 坐标值之和 sumx,那么这些点 X 方向上对结果的贡献为 rankx * xi - sumx;同理可以处理出 x 坐标大于 xi 的点在 X 方向上对结果的贡献值。同理可求得其余点在 Y 方向上到达 yi 所需要的总时间。

悲催,比赛剩两个小时都在想这一个题,然后就是没想起来。看有人说的这个题也可以蹭数据过去http://blog.csdn.net/cscj2010/article/details/7791640今晚看见的一个解题报告,不过这个是接下来要说的一个题目的解题报告

这个题纠结的一个地方,一开始把x 和 y 坐标都定义成了 long long 型,然后用 %d 和 %I64d 输入编译不过,用 %lld输入,交上去就WA,用cin 输入就过了,真是很无语。其实定义成 int 就可以了

View Code
 1 #include <iostream>
 2 #include <string.h>
 3 #include <stdio.h>
 4 #include <algorithm>
 5 #define N 100010
 6 #define _clr(a,val) (memset(a,val,sizeof(a)))
 7 
 8 using namespace std;
 9 
10 typedef long long ll;
11 struct node
12 {
13     int x;
14     int y;
15     ll index;
16 }point[N],tpoint[N];
17 ll sum[N],sumx[N],sumy[N];
18 int cmp1(node a,node b)
19 {
20     return a.x < b.x;
21 }
22 int cmp2(node a,node b)
23 {
24     return a.y < b.y;
25 }
26 ll minn(ll a,ll b)
27 {
28     if(a < b) return a;
29     else return b;
30 }
31 int main()
32 {
33     int n,t;
34     ll i;
35     //freopen("data.txt","r",stdin);
36     //cin>>t;
37     scanf("%d",&t);
38     while(t--)
39     {
40         _clr(sumx,0);
41         _clr(sumy,0);
42         //cin>>n;
43         scanf("%d",&n);
44         for(i = 1; i <= n; i++)
45         {
46             scanf("%d%d",&point[i].x,&point[i].y);
47             //cin>>point[i].x>>point[i].y;
48             point[i].index = i;
49             tpoint[i] = point[i];
50         }
51         sort(point + 1,point + n + 1,cmp1);
52         sort(tpoint + 1,tpoint + n + 1,cmp2);
53         _clr(sum,0);
54         for(i = 1; i <= n; i++)
55         sum[i] = sum[i - 1] + point[i].x;
56         for(i = 1; i <= n; i++)
57         {
58             sumx[point[i].index] = (((i - 1) * point[i].x - sum[i - 1]) + ((sum[n] - sum[i]) - (n - i) * point[i].x));
59         }
60         _clr(sum,0);
61         for(i = 1; i <= n; i++)
62         sum[i] = sum[i - 1] + tpoint[i].y;
63         for(i = 1; i <= n; i++)
64         {
65             sumy[tpoint[i].index] = (((i - 1) * tpoint[i].y - sum[i - 1]) + ((sum[n] - sum[i]) - (n - i) * tpoint[i].y));
66         }
67         ll ans = sumx[1] + sumy[1];
68         for(i = 2; i <= n; i++)
69         ans = minn(ans,sumx[i] + sumy[i]);
70         cout<<ans<<endl;
71     }
72     return 0;
73 }

题目:http://acm.hdu.edu.cn/showproblem.php?pid=4312

题意:跟上一个题意思差不多,但是这里的距离是定义的切比雪夫距离 dis =  max(|x1-x2|, |y1-y2|).

思路:如果不看标程,还真想不到把坐标转换,最后计算一半的曼哈顿距离就行了。 官解:

平面上两点间的 Chebyshev距离为 max(|x1-x2|, |y1-y2|)

                      

Chebyshev                                                              Manhattan

 

对于原坐标系中两点间的 Chebyshev 距离,是将坐标轴顺时针旋转45度并将所有点的坐标值放大sqrt(2)倍所得到的新坐标系中的Manhattan距离的二分之一。

大家可以画图想一下……

假设有两点(x1,y1), (x2,y2),不妨设 x1>x2(否则交换这两点即可)。

则Chebyshev距离 D1 = max(|x1-x2|, |y1-y2|)

这两点个对应到新坐标系中的坐标为 (x1-y1, x1+y1), (x2-y2, x2+y2)

则Manhattan 距离D2 = |x1-y1-x2+y2| + |x1+y1-x2-y2|

View Code
 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <iostream>
 4 #include <algorithm>
 5 #define N 100010
 6 #define _clr(a,val) (memset(a,val,sizeof(val)))
 7 
 8 using namespace std;
 9 
10 typedef long long ll;
11 struct node
12 {
13     int x;
14     int y;
15     int index;
16 }point[N],tpoint[N];
17 ll sum[N],sumy[N],sumx[N];
18 int cmp1(node a,node b)
19 {
20     return a.x < b.x;
21 }
22 int cmp2(node a,node b)
23 {
24     return a.y < b.y;
25 }
26 int main()
27 {
28     ll i,j;
29     int t,n;
30     int x,y;
31     //freopen("data.txt","r",stdin);
32     scanf("%d",&t);
33     while(t--)
34     {
35         scanf("%d",&n);
36         for(i = 1; i <= n; i++)
37         {
38             //scanf("%d%d",&point[i].x,&point[i].y);
39             scanf("%d%d",&x,&y);
40             point[i].x = x - y;
41             point[i].y = x + y;
42             point[i].index = i;
43             tpoint[i] = point[i];
44         }
45         sort(point + 1,point + n + 1,cmp1);
46         sort(tpoint + 1,tpoint + n + 1,cmp2);
47         _clr(sum,0);
48         for(i = 1; i <= n; i++)
49         sum[i] = sum[i - 1] + point[i].x;
50         _clr(sumx,0);
51         for(i = 1; i <= n; i++)
52         {
53             sumx[point[i].index] = ((((i - 1) * point[i].x) - sum[i - 1]) + ((sum[n] - sum[i]) - (n - i) * point[i].x));
54         }
55         _clr(sum,0);
56         for(i = 1; i <= n; i++)
57         sum[i] = sum[i - 1] + tpoint[i].y;
58         _clr(sumy,0);
59         for(i = 1; i <= n; i++)
60         {
61             sumy[tpoint[i].index] = ((((i - 1) * tpoint[i].y) - sum[i - 1]) + ((sum[n] - sum[i]) - (n - i) * tpoint[i].y));
62         }
63         ll ans = (sumx[1] + sumy[1]);
64         for(i = 2; i <= n; i++)
65         ans = min(ans,(sumx[i] + sumy[i]));
66         cout<<ans / 2<<endl;
67     }
68     return 0;
69 }

题目:http://acm.hdu.edu.cn/showproblem.php?pid=4313

题意:有n个节点的树,有n - 1条边,给你一些点,问如何删边(删除的这些边权值之和最小),可以使得所给的k个点互不联通,

思路:并查集 + kruskal最小生成树过程,不过此处将边按权值从大到小排列,每次将边加进来时要判断是否会使两个危险的点连通,是的话这条边就是需要被删除的,否则将它加到树上。

看了解题报告,自己还是错了n次,都是不细心,数组赋错值,排序排错,唉,wa了老半天

View Code
 1 #include <iostream>
 2 #include <string.h>
 3 #include <stdio.h>
 4 #include <algorithm>
 5 #define N 100010
 6 #define _clr(a,val) (memset(a,val,sizeof(a)))
 7 
 8 using namespace std;
 9 
10 typedef long long ll;
11 struct node
12 {
13     int s;
14     int e;
15     int wei;
16 }eage[N];
17 int cmp(node a,node b)
18 {
19     return a.wei > b.wei;
20 }
21 int f[N];
22 bool v[N];
23 int find(int x)
24 {
25     if(x != f[x]) return find(f[x]);
26     return x;
27 }
28 int main()
29 {
30     int i,j;
31     int n,t,m;
32     //freopen("data.txt","r",stdin);
33     scanf("%d",&t);
34     while(t--)
35     {
36         scanf("%d%d",&n,&m);
37         for(i = 0; i < n; i++)
38         f[i] = i;
39         for(i = 0; i < n - 1; i++)
40         {
41             scanf("%d%d%d",&eage[i].s,&eage[i].e,&eage[i].wei);
42         }
43         sort(eage,eage + n - 1,cmp);
44         int x;
45         _clr(v,false);
46         for(i = 0; i < m; i++)
47         {
48             scanf("%d",&x);
49             v[x] = 1;
50         }
51         ll ans = 0;
52         for(i = 0; i < n - 1; i++)
53         {
54             int root1 = find(eage[i].s);
55             int root2 = find(eage[i].e);
56             if(v[root1] && v[root2])
57             {
58                 ans += eage[i].wei;
59                 continue;
60             }
61             f[root1] = root2;
62             if(v[root1])
63             {
64                 v[root2] = 1;
65             }
66         }
67         cout<<ans<<endl;
68     }
69     return 0;
70 }

题目:http://acm.hdu.edu.cn/showproblem.php?pid=4315

思路:这个完全是看解题报告的,当时觉得要考虑king的位置,挺麻烦的,没想到竟然不用考虑,类似与nim游戏

官方的解释:

中等博弈题。此题的简化版本是不考虑King的存在,双方一直走到不能走的一方为负。此时的解法是根据人数的奇偶性:把人从上顶向下的位置记为a1,a2,...an, 如果为偶数个人,则把a(2i-1)和a(2i)之间的距离当做一个Nim堆,变成一共n/2堆的Nim游戏;如果为奇数个人,则把山顶到a1的距离当做一个Nim堆,a(i*2)到a(i*2+1)的距离当做Nim堆,一共(n+1)/2堆。

 考虑King的情况和上述版本几乎一致,只要把King当作普通人一样处理即可。除了两种特殊情况:1. 当King是第一个人时,Alice直接胜 2. 当King是第二个人且一共有奇数个人时,第一堆的大小需要减1。

View Code
 1 #include <cstdio>
 2 #include <string>
 3 #include <cstring>
 4 #define N 1010
 5 
 6 using namespace std;
 7 
 8 int a[N];
 9 int main() {
10   //freopen("data.txt","r",stdin);
11     int n, k, i;
12     while (scanf("%d%d",&n,&k) != EOF)
13     {
14         int nim = 0;
15         //int tem;
16         for (i = 0 ; i < n ; i++)
17          {
18             scanf("%d",&a[i]);
19         }
20         if (k == 1) {printf("Alice\n"); continue;}
21         else
22         {
23             int tem = a[0];
24             if(k == 2 && n % 2)
25             {
26                 tem = a[0] - 1;
27             }
28             if(n % 2 == 0)
29             for(i = 0; i < n; i += 2)
30             nim ^= (a[i + 1] - a[i] - 1);
31             else
32             {
33                 nim ^= tem;
34                 for(i = 1; i < n; i += 2)
35                 nim ^= (a[i + 1] - a[i] - 1);
36             }
37         }
38         //printf("%d\n",nim);
39         if (nim) printf("Alice\n");
40         else printf("Bob\n");
41     }
42     return 0;
43 }

题目:http://acm.hdu.edu.cn/showproblem.php?pid=4318

题意:可大致表述为从节点s向节点t传送电力,电力在传送过程中会有所消耗,不同节点之间,电力传送消耗的值有所不同。要求选择一条

使得电力消耗最小的线路。如果不能把电力从s点传送到t点,或者电力损失殆尽,则输出IMPOSSIBLE!

解法1:我们要求损耗最小,也就是剩余最大。对于每个节点,我们记录起当前可以达到的剩余最大电力,我们这里每次找寻的是尚未标记的拥有最大值的结点,并把这个最大值作为当前结点的最终结果,标记此结点并通过当前结点拓展与之相连的结点,最后输出的时候记住转换

 

View Code
 1 #include <iostream>
 2 #include <string.h>
 3 #include <stdio.h>
 4 #include <algorithm>
 5 #include <queue>
 6 #define N 50010
 7 #define inf 10000010
 8 #define _clr(a,val) (memset(a,val,sizeof(a)))
 9 
10 using namespace std;
11 
12 typedef long long ll;
13 struct node
14 {
15     int from;
16     int to;
17     int wei;
18     int next;
19 }eage[N * 50];
20 int head[N];
21 double dis[N];
22 bool v[N];
23 int num,n;
24 void add(int f,int t,int w)
25 {
26     eage[num].from = f;
27     eage[num].to = t;
28     eage[num].wei = w;
29     eage[num].next = head[f];
30     head[f] = num++;
31 }
32 void spfa(int ss,int val)
33 {
34     queue<int>qu;
35     while(!qu.empty()) qu.pop();
36     for(int i = 1; i <= n; i++)
37     {
38         dis[i] = -inf;
39         v[i] = false;
40     }
41     dis[ss] = val;
42     qu.push(ss);
43     v[ss] = 1;
44     while(!qu.empty())
45     {
46         int tem = qu.front();
47         qu.pop();
48         v[tem] = false;
49         for(int i = head[tem]; i != -1; i = eage[i].next)
50         {
51             int tt = eage[i].to;
52             double temp = (dis[tem] * (100 - eage[i].wei)) / (100 * 1.0);
53             if(temp > dis[tt])
54             {
55                 dis[tt] = temp;
56                 if(!v[tt])
57                 {
58                     v[tt] = true;
59                     qu.push(tt);
60                 }
61             }
62         }
63     }
64 }
65 int main()
66 {
67     int i,k;
68     int f,t;
69     int s,e,w;
70     //freopen("data.txt","r",stdin);
71     while(scanf("%d",&n) != EOF)
72     {
73         num = 0;
74         _clr(head,-1);
75         for(i = 1; i <= n; i++)
76         {
77             scanf("%d",&k);
78             while(k--)
79             {
80                 scanf("%d%d",&f,&t);
81                 add(i,f,t);
82             }
83         }
84         scanf("%d%d%d",&s,&e,&w);
85         spfa(s,w);
86         /*for(i = 1; i <= n; i++)
87         cout<<dis[i]<<" ";
88         cout<<endl;*/
89         if(dis[e] == -inf) printf("IMPOSSIBLE!\n");
90         else printf("%.2lf\n",w * 1.0 - dis[e]);
91     }
92     return 0;
93 }

解法2:我们可以把乘积的形式通过取对数化作连续相加的形式   即log(1-b1%) +…+log(1-bn+1%),由于 log(1-bi%)都是小于0的,我们要求这个式子的最大值就是求每个子式取绝对值的最小值。所以通过取对数在取绝对值的操作,我们可以得到两节点之间新的边权。同时题目也转化为求单源最短路问题,用的方法跟上一种一样,都是用spfa来求最短路,最后注意把结果进行转化。

View Code
 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <iostream>
 4 #include <algorithm>
 5 #include <queue>
 6 #include <math.h>
 7 #include <stdlib.h>
 8 #define N 50010
 9 #define inf 1000000
10 #define _clr(a,val) (memset(a,val,sizeof(a)))
11 
12 using namespace std;
13 
14 struct node
15 {
16     int from;
17     int to;
18     double wei;
19     int next;
20 }eage[N * 50];
21 bool v[N];
22 int head[N];
23 double dis[N];
24 int s,e,val;
25 int num,n,m;
26 void add(int f,int t,double w)
27 {
28     eage[num].from = f;
29     eage[num].to = t;
30     eage[num].wei = w;
31     eage[num].next = head[f];
32     head[f] = num++;
33 }
34 void spfa()
35 {
36     queue<int>qu;
37     for(int i = 1; i <= n; i++)
38     {
39         v[i] = false;
40         dis[i] = inf;
41     }
42     v[s] = true, dis[s] = 0;
43     qu.push(s);
44     while(!qu.empty())
45     {
46         int tem = qu.front();
47         qu.pop();
48         v[tem] = false;
49         for(int i = head[tem]; i != -1; i = eage[i].next)
50         {
51             int tt = eage[i].to;
52             double temp = dis[tem] + eage[i].wei;
53             if(dis[tt] > temp)
54             {
55                 dis[tt] = temp;
56                 if(!v[tt])
57                 {
58                     v[tt] = true;
59                     qu.push(tt);
60                 }
61             }
62         }
63     }
64 }
65 int main()
66 {
67     int i;
68     int f,t;
69     int k;
70     //freopen("data.txt","r",stdin);
71     while(scanf("%d",&n) != EOF)
72     {
73         num = 0;
74         _clr(head,-1);
75         for(i = 1; i <= n; i++)
76         {
77             scanf("%d",&k);
78             while(k--)
79             {
80                 scanf("%d%d",&f,&t);
81                 add(i,f, -log((1.0 - (double)(t) / 100.0)));
82             }
83         }
84         scanf("%d%d%d",&s,&e,&val);
85         spfa();
86         if(dis[e] == inf) printf("IMPOSSIBLE\n");
87         else printf("%.2lf\n",val * (1.0 - exp(-dis[e])));
88     }
89     return 0;
90 }

 

 

 

 

 

 

 

posted @ 2012-07-27 21:42  AC_Girl  阅读(210)  评论(0编辑  收藏  举报