Hdu 5385 The path
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5385
题意:有一个联通的有向图,d(x)用来记录从1点到x点的最短路径长度,d(1)=0;一个图可以称之为好图是存在一个x使得d(1)<d(2)<....d(x)>d(x+1)>...d(n), 现在你要设置每一条边的长度使得这个图是一个好图,注意,满足d(1)<d(2)<..d(n)的也是一个好图。边的长度在1~n的范围内。一定存在解决方案。
思路:按照题解说的模拟构造就行了,如下: 我们可以采取贪心做法,一开始将1号点作为最短路径树的根,然后左边从2开始,右边从n开始,只要之前加入的点有边连向他们就加入,这样一个点加入的时间就是他的dis值,最短路径树上的父亲也可以确定,于是输出时非树边长度为n,树边长度为两个端点dis之差
参考代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <iostream> 2 #include <cmath> 3 #include <algorithm> 4 #include <cstring> 5 #include <string> 6 #include <cstdio> 7 #include <vector> 8 using namespace std; 9 #define M 300010 10 int dis[M], f[M], head[M]; 11 int cnt; 12 struct node 13 { 14 int from, to, next; 15 } edge[M * 2 + 10]; 16 void addedge(int a, int b) 17 { 18 edge[cnt].from = a; 19 edge[cnt].to = b; 20 edge[cnt].next = head[a]; 21 head[a] = cnt++; 22 } 23 void add(int x) 24 { 25 for (int i = head[x]; i!=-1; i = edge[i].next) 26 { 27 int v = edge[i].to; 28 if (!f[v]) 29 f[v] = x; 30 } 31 } 32 int main() 33 { 34 int T, n, m, a, b; 35 scanf("%d", &T); 36 while (T--) 37 { 38 memset(f, 0, sizeof(f)); 39 memset(head, -1, sizeof(head)); 40 f[1] = -1; 41 dis[1] = 0; 42 cnt = 0; 43 scanf("%d%d", &n, &m); 44 for (int i = 0; i < m; i++) 45 { 46 scanf("%d%d",&a, &b); 47 addedge(a, b); 48 } 49 int num = 1, l = 1, r = n; 50 while (l <= r) 51 { 52 if (f[l]) 53 { 54 add(l); 55 dis[l++] = num++; 56 } 57 if (f[r]) 58 { 59 add(r); 60 dis[r--] = num++; 61 } 62 } 63 for (int i = 0; i < cnt; i++) 64 { 65 a = edge[i].from; 66 b = edge[i].to; 67 if (f[b] != a)//不在最短路树上的边 68 printf("%d\n", n); 69 else 70 printf("%d\n", dis[b] - dis[a]);//最短路树上的边 71 } 72 } 73 return 0; 74 }
Hdu 5386 Cover
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5386
题意:有一个n*n的矩阵,矩阵的每一个方格都有颜色,有以下两种操作:
L x y: for(int i=1;i<=n;i++)color[i][x]=y;
H x y:for(int i=1;i<=n;i++)color[x][i]=y;
现在给你矩阵的初始状态和最终状态,m种操作,写出这些操作的顺序使的经过这些操作后,矩阵从初始状态变成最终状态;
思路:逆向思维。
最后一个操作肯定是把某一行或者某一列变成x,我们倒过来模拟,每次把最后一个操作找出来,即每次找到某一行或者某一列不为0的数都相同的,再找符合操作的。
参考代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <iostream> 2 #include <cmath> 3 #include <algorithm> 4 #include <cstring> 5 #include <string> 6 #include <cstdio> 7 #include <vector> 8 using namespace std; 9 #define M 505 10 #define N 105 11 #define MOD 258280327 12 int n, m, h, l; 13 #define inf 0x3f3f3f3f 14 bool vish[M], visl[M]; 15 int G[N][N]; 16 struct node 17 { 18 int color, id, x; 19 } oph[M], opl[M]; 20 int path[M]; 21 int judge(int x, int color, int dix) 22 { 23 if (dix == 1) 24 { 25 for (int i = 1; i <= n; i++) 26 { 27 if (G[x][i] == inf) 28 continue; 29 if (G[x][i] != color) 30 return false; 31 } 32 } 33 else 34 { 35 for (int i = 1; i <= n; i++) 36 { 37 if (G[i][x] == inf) 38 continue; 39 if (G[i][x] != color) 40 return false; 41 } 42 } 43 } 44 void setcolor(int x, int dix) 45 { 46 if (dix == 1) 47 { 48 for (int i = 1; i <= n; i++) 49 G[x][i] = inf; 50 } 51 else 52 { 53 for (int i = 1; i <= n; i++) 54 G[i][x] = inf; 55 } 56 } 57 void solve() 58 { 59 int cnt = 0; 60 while (cnt < m) 61 { 62 for (int i = 0; i < h; i++)//试每一种没有使用过的纵向操作 63 { 64 if (vish[i]) 65 continue; 66 if (judge(oph[i].x, oph[i].color, 1)) 67 { 68 setcolor(oph[i].x, 1); 69 vish[i] = true; 70 path[cnt++] = oph[i].id;//把操作序号记录下来 71 } 72 } 73 for (int i = 0; i < l; i++) 74 { 75 if (visl[i]) 76 continue; 77 if (judge(opl[i].x, opl[i].color, 2)) 78 { 79 setcolor(opl[i].x, 2); 80 visl[i] = true; 81 path[cnt++] = opl[i].id; 82 } 83 } 84 } 85 } 86 int main() 87 { 88 int T; 89 scanf("%d", &T); 90 while (T--) 91 { 92 scanf("%d%d", &n, &m); 93 int temp; 94 //初始状态是不用记录的 95 for (int i = 1; i <= n; i++) 96 for (int j = 1; j <= n; j++) 97 scanf("%d", &temp); 98 for (int i = 1; i <= n; i++) 99 { 100 for (int j = 1; j <= n; j++) 101 scanf("%d", &G[i][j]); 102 } 103 memset(vish, 0, sizeof(vish));//记录纵向操作是否操作过 104 memset(visl, 0, sizeof(visl));//记录横向操作是否操作过 105 char op[2]; 106 int x, color; 107 h = 0; 108 l = 0; 109 for (int i = 1; i <= m; i++) 110 { 111 scanf("%s%d%d", op, &x, &color); 112 if (op[0] == 'H') 113 { 114 oph[h].x = x; 115 oph[h].color = color; 116 oph[h++].id = i; 117 } 118 else 119 { 120 opl[l].x = x; 121 opl[l].color = color; 122 opl[l++].id = i; 123 } 124 } 125 solve(); 126 //打印路径 127 for (int i = m - 1; i >= 0; i--) 128 { 129 if (i == m - 1) 130 printf("%d", path[i]); 131 else 132 printf(" %d", path[i]); 133 } 134 printf("\n"); 135 } 136 return 0; 137 }
Hdu 5387 Clock
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5387
题意:给你一个时间,输出时分秒三个指针之间的角度(0~180),注意:实数用最简分数表示,整数直接输出。
思路:简单题。
参考代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <iostream> 2 #include <cmath> 3 #include <algorithm> 4 #include <cstring> 5 #include <string> 6 #include <cstdio> 7 #include <vector> 8 using namespace std; 9 #define ang 3600 10 typedef long long ll; 11 int gcd(int a,int b){ 12 return b==0?a:gcd(b,a%b); 13 } 14 void calangle(int a,int b) 15 { 16 int sum; 17 if(abs(a-b)>30*6*ang) 18 sum=60*6*ang-abs(a-b); 19 else 20 sum=abs(a-b); 21 if(sum%ang) 22 { 23 int d=gcd(sum,ang); 24 printf("%d/%d ",sum/d,ang/d); 25 } 26 else 27 printf("%d ",sum/ang); 28 } 29 int main() 30 { 31 int i,j,k,h,m,s,t; 32 char c; 33 scanf("%d",&t); 34 while(t--) 35 { 36 scanf("%d%c%d%c%d",&h,&c,&m,&c,&s); 37 if(h>=12) 38 h=h-12; 39 int ss=ang*s*6; 40 int mm=(60*s+ang*m)*6; 41 int hh=((s+60*m)*5+h*ang*5)*6; 42 calangle(hh,mm); 43 calangle(hh,ss); 44 calangle(mm,ss); 45 printf("\n"); 46 } 47 return 0; 48 }
Hdu 5387 Zero Escape
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5389
题意:给你n个人每个人手里有一个id,然后给你两个数a和b,让你把n个人分为两组,条件是 一组人手里的id和等于a 另一组人的id和等于b,这里的和是指加起来之后对9取余,如果sum等于0 则sum等于9 否则sum = sum;还有一种情况也可以 就是所有人的id和等于a 或者等于b 相当于分为一组。
思路:
dp[i][j]=dp[i-1][j]+dp[i-1][tmp](tmp为j-arr[i],若小于等于0,需加9调整)
dp[i][j]含义为取前i个数字中的若干个,按给定规则,算出来的和为j的方案数
之前有两个细节没考虑到,
一是、当前位和我dp[i][j]中的j值相同,那么对应三种情况。
1.当前位不取,直接取前面dp[i-1][j]的值
2.当前位取,前面取的是dp[i-1][9]的值(因为加9,不变)
3.当前位取,前面都不取,此种情况特殊,直接加一即可。
其实2、3两种情况是一个数分别对应dp[i-1][0]和dp[i-1][9]的特殊情况。
二是、当a+b的和和sum相同时,最后输出结果的时候,按理说只要输出dp[n][a]就可以了,但是这样会遗漏a中都不取,全都放在b中,而b又刚好和sum相同的情况。
参考代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <iostream> 2 #include <cmath> 3 #include <algorithm> 4 #include <cstring> 5 #include <string> 6 #include <cstdio> 7 #include <vector> 8 using namespace std; 9 #define M 100005 10 #define MOD 258280327 11 int num[M]; 12 int dp[M][10]; 13 int cal(int n) 14 { 15 int k=0; 16 while(n) 17 { 18 k+=(n%10); 19 n/=10; 20 } 21 if(k>9) 22 return cal(k); 23 else 24 return k; 25 } 26 int main() 27 { 28 int T,a,b,n,temp,sum; 29 scanf("%d",&T); 30 while(T--) 31 { 32 memset(dp,0,sizeof(dp)); 33 sum=0; 34 scanf("%d%d%d",&n,&a,&b); 35 for(int i=1;i<=n;i++) 36 { 37 scanf("%d",&num[i]); 38 num[i]=cal(num[i]); 39 sum+=num[i]; 40 } 41 sum=cal(sum); 42 int s=a+b; 43 s=cal(s); 44 if(sum==s) 45 { 46 dp[1][num[1]]=1; 47 for(int i=2;i<=n;i++) 48 { 49 for(int j=1;j<=9;j++) 50 { 51 temp=j-num[i]; 52 if(temp<=0) 53 temp+=9; 54 dp[i][j]=(dp[i-1][temp]+dp[i-1][j])%MOD; 55 } 56 } 57 printf("%d\n",(dp[n][a]+dp[n][b])%MOD); 58 } 59 else 60 { 61 int ans=0; 62 if(sum==a) 63 ans++; 64 if(sum==b) 65 ans++; 66 printf("%d\n",ans); 67 } 68 } 69 return 0; 70 }