题目:http://acm.hdu.edu.cn/showproblem.php?pid=4308
题意:王子找公主。在一个图里面,如果是 * 则表示可以走,而且没走一个 * 需要的话费是 c 元,如果是 # 则不能通过,如果是 P 则可以跳到其他任意一个 P 所在的点,王子是 Y 表示,公主是 C 表示,问王子找到公主的最小花费。
思路:bfs,跟普通的bfs一样,但是由于题目里面说 P 与 P 是可以任意到达的(而且不需要任何花费),所以要特殊处理,用一个队保存可行点,从当前点开始搜索,如果遇到 * 则进队继续搜索,如果遇到 P 则要把图里面所有的 P 都搜出来进队,然后继续搜索。因为题目说 r * c <= 5000 ,怕超内存,这里我把二维的数组改成了一维的,下标进行了转换。不过貌似看网上有人也用的二维的
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不太知道,所以只能贴过来了
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 就可以了
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|
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了老半天
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。
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:我们要求损耗最小,也就是剩余最大。对于每个节点,我们记录起当前可以达到的剩余最大电力,我们这里每次找寻的是尚未标记的拥有最大值的结点,并把这个最大值作为当前结点的最终结果,标记此结点并通过当前结点拓展与之相连的结点,最后输出的时候记住转换
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来求最短路,最后注意把结果进行转化。
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 }