D.UVa12880
解题关键:dfs,判断每个人是否愿意被其他人交换,注意保证每个人只能被交换一次。
1 #include<cstdio>
2 #include<cstring>
3 #include<algorithm>
4 #include<cstdlib>
5 #include<cmath>
6 #include<iostream>
7 #define maxn 220000
8 using namespace std;
9 typedef long long ll;
10 struct Edge{
11 int nxt;
12 int to;
13 }e[maxn];
14 int head[maxn],cnt,pre[maxn];
15 bool vis[maxn];
16 void add_edge(int u,int v){//单向
17 e[cnt].to=v;
18 e[cnt].nxt=head[u];
19 head[u]=cnt++;
20 }
21 bool dfs(int u){
22 for(int i=head[u];i!=-1;i=e[i].nxt){
23 int v=e[i].to;
24 if(vis[v]) continue;
25 vis[v]=true;
26 if(pre[v]==-1||dfs(pre[v])){
27 pre[v]=u;
28 return true;
29 }
30 }
31 return false;
32 }
33 int main(){
34 int n,m,a,b;
35 while(scanf("%d%d",&n,&m)!=EOF){
36 memset(head, -1, sizeof head);
37 memset(pre,-1,sizeof pre);
38 cnt=0;
39 for(int i=0;i<m;i++){
40 scanf("%d%d",&a,&b);
41 add_edge(a,b);
42 }
43 for(int i=0;i<n;i++){
44 memset(vis,0,sizeof vis);
45 dfs(i);
46 }
47 bool flag=false;
48 for(int i=0;i<n;i++){
49 if(pre[i]==-1){
50 flag=true;
51 break;
52 }
53 }
54 if(flag) printf("NO\n");
55 else printf("YES\n");
56 }
57 return 0;
58 }
View Code
E.hdu4313
解题关键:正向的思路是将边从小到大排序,如果去掉某条边,两个特殊点并起来了,则需去掉该边,我们反向考虑,将边从大到小排序,依次向图中加边,如果两个点并起来了,则需去掉该边。
1 #include<cstdio>
2 #include<cstring>
3 #include<algorithm>
4 #include<cstdlib>
5 #include<iostream>
6 #include<vector>
7 #include<cmath>
8 #define maxn 100020
9 using namespace std;
10 typedef long long ll;
11 int par[maxn],n,k;
12 //bool vis[maxn];
13 struct edge{
14 int u;
15 int v;
16 int w;
17 }e[maxn];
18 bool flag[maxn];
19 bool cmp(const edge &a,const edge &b){
20 return a.w>b.w;
21 }
22 void init1(){
23 for(int i=0;i<=n;i++) par[i]=i;
24 }
25
26 int find1(int x){
27 if(par[x]==x) return x;
28 else return par[x]=find1(par[x]);
29 }
30
31 void unite(int x,int y){
32 par[x]=y;
33 }
34
35 int main(){
36 int t,a;
37 ios::sync_with_stdio(0);
38 cin>>t;
39 while(t--){
40 memset(flag, 0, sizeof flag);
41 cin>>n>>k;
42 init1();
43 for(int i=0;i<n-1;i++){
44 cin>>e[i].u>>e[i].v>>e[i].w;
45 }
46 sort(e,e+n-1,cmp);
47 for(int i=0;i<k;i++){
48 cin>>a;
49 flag[a]=1;
50 }
51 ll ans=0;
52 for(int i=0;i<n-1;i++){
53 if(flag[find1(e[i].u)]&&flag[find1(e[i].v)]){
54 ans+=e[i].w;
55 }
56 else if(flag[find1(e[i].u)]){
57 unite(find1(e[i].v),find1(e[i].u));
58 }else{
59 unite(find1(e[i].u), find1(e[i].v));
60 }
61 }
62 cout<<ans<<"\n";
63 }
64 return 0;
65 }
View Code
F.hdu4324
解题关键:
求是否存在三元环
如果存在环,则一定存在三元环 (scc和拓扑排序均可)
证明如下:
不存在二元环
设存在 $n(n>=3)$元环 $p_1->p_2->p_3->…->p_n->p_1$
1) 若存在边 $p_3->p_1$,则存在三元环 $(p_1->p_2->p_3->p_1)$
2) 若不存在 $p_3->p_1$,则必然存在 $p_1->p_3$
那么 $p_1->p_3->…->p_n->p_1$又构成 $n-1$元环
递归证明可得,如果存在环,必然存在三元环。
1、targinSCC
1 #include<iostream>
2 #include<cstring>
3 #include<cstdio>
4 #include<cstdlib>
5 #include<cmath>
6 #include<algorithm>
7 using namespace std;
8 typedef long long ll;
9 #define MAXN 2010
10 #define MAXM 4000020
11 struct edge{
12 int to,nxt;
13 }e[MAXM];
14 //int being[MAXN];
15 int head[MAXN],st[MAXN],dfn[MAXN],lowest[MAXN],belong[MAXN];
16 bool inst[MAXN];
17 int t,n,m,scnt,top,tot;//scnt从1开始
18 void init(){
19 memset(head,-1,sizeof head);
20 memset(dfn,0,sizeof dfn);//记住初始化
21 // memset(being, 0, sizeof being);
22 scnt=top=tot=0;
23 }
24
25 void add_edge(int u, int v){
26 e[tot].to=v;
27 e[tot].nxt=head[u];
28 head[u]=tot++;
29 }
30
31 void Tarjan(int u){
32 dfn[u]=lowest[u]=++tot;
33 inst[u]=1;
34 st[top++]=u;
35 for(int i=head[u];i!=-1;i=e[i].nxt){
36 int v=e[i].to;
37 if(!dfn[v]){
38 Tarjan(v);
39 lowest[u]=min(lowest[u],lowest[v]);
40 }
41 else if(inst[v]){
42 lowest[u]=min(lowest[u],dfn[v]);//也可用lowest
43 }
44 }
45 if(dfn[u]==lowest[u]){
46 scnt++;
47 int t;
48 do{
49 t=st[--top];
50 inst[t]=false;
51 belong[t]=scnt;
52 }while(t!=u);
53 }
54 }
55
56 void solve(){//缩点
57 for(int i=1;i<=n;i++) if(!dfn[i]) Tarjan(i);
58 /* for(int i=1;i<=n;i++){
59 being[belong[i]]++;
60 }
61 bool flag=false;
62 for(int i=1;i<=scnt;i++){
63 if(being[i]>1){
64 flag=true;
65 break;
66 }
67 }
68 */
69 if(scnt!=n) printf("Yes\n");
70 //if(flag) printf("Yes\n");
71 else printf("No\n");
72 }
73 char ch[2200];
74 int main(){
75 scanf("%d",&t);
76 for(int i=1;i<=t;i++){
77 init();
78 printf("Case #%d: ",i);
79 scanf("%d",&n);
80 for(int j=1;j<=n;j++){
81 scanf("%s",ch);
82 for(int k=0;k<n;k++){
83 if(ch[k]=='1') add_edge(j,k+1);
84 }
85 }
86 solve();
87 }
88 return 0;
89 }
View Code
2、toposort
1 #include<cstdio>
2 #include<cstring>
3 #include<algorithm>
4 #include<cstdlib>
5 #include<iostream>
6 #include<cmath>
7 #include<vector>
8 #include<queue>
9 using namespace std;
10 typedef long long ll;
11 const int N=4000200;
12 priority_queue<int,vector<int>,greater<int> >q;//因为是按字典序的,要注意重边的情况
13 int deg[2002],seq[2002];
14 int n,m,u,v,tol,t;
15 struct Edge{
16 int nxt;
17 int to;
18 int w;
19 }e[N];
20 int head[N],cnt;
21 void add_edge(int u,int v){
22 e[cnt].to=v;
23 e[cnt].nxt=head[u];
24 head[u]=cnt++;
25 }
26 bool toposort(){
27 for(int i=1;i<=n;i++)if(!deg[i])q.push(i);
28 tol=0;
29 while(q.size()){
30 int u=q.top();
31 q.pop();
32 seq[tol++]=u;
33 for(int i=head[u];i!=-1;i=e[i].nxt){
34 int v=e[i].to;
35 deg[v]--;
36 if(!deg[v]) q.push(v);
37 }
38 }
39 if(tol==n) return false;
40 else return true;
41 }
42 char ch[2002];
43 int main(){
44 scanf("%d",&t);
45 for(int i=1;i<=t;i++){
46 memset(head, -1, sizeof head);
47 memset(deg, 0, sizeof deg);
48 cnt=0;
49 scanf("%d",&n);
50 for(int j=1;j<=n;j++){
51 scanf("%s",ch);
52 for(int k=0;k<n;k++){
53 if(ch[k]=='1') add_edge(j,k+1),deg[k+1]++;
54 }
55 }
56 bool flag=toposort();
57 printf("Case #%d: ",i);
58 if(flag) printf("Yes\n");
59 else printf("No\n");
60 }
61 return 0;
62 }
View Code
G.hdu4318
解题关键:最短路转化为最长路。
1 #include<cstdio>
2 #include<cstring>
3 #include<algorithm>
4 #include<cstdlib>
5 #include<iostream>
6 #include<cmath>
7 #include<queue>
8 using namespace std;
9 const int maxn=50002;
10 const int maxm=2600010;
11 int head[maxn],tot,n,m;
12 struct edge{
13 int to;
14 double w;
15 int nxt;
16 }e[maxm];
17 void add_edge(int u,int v,double w){
18 e[tot].w=w;
19 e[tot].to=v;
20 e[tot].nxt=head[u];
21 head[u]=tot++;
22 }
23 bool vis[maxn];
24 queue<int>que;//队列是点的队列
25 double d[maxn];
26 void spfa(int s){
27 for(int i=0;i<=n;i++) d[i]=0.0;
28 memset(vis,0,sizeof vis);
29 while (!que.empty()) que.pop();
30 que.push(s);
31 vis[s]=true;
32 d[s]=1.0;
33 while (!que.empty()){
34 int u=que.front();
35 que.pop();
36 vis[u]=false;
37 for (int i=head[u];i!=-1;i=e[i].nxt){
38 int v=e[i].to;
39 double w=e[i].w;
40 if (d[v]<d[u]*w){
41 d[v]=d[u]*w;
42 if (!vis[v]){
43 vis[v]=true;
44 que.push(v);//hash一下,可判断是否存在负环
45 }
46 }
47 }
48 }
49 }
50 int main(){
51 int a,b,k,td;
52 //double c;
53 while(scanf("%d",&n)!=EOF){
54 memset(head, -1, sizeof head);
55 tot=0;
56 for(int i=0;i<n;i++){
57 scanf("%d",&k);
58 for(int j=0;j<k;j++){
59 scanf("%d%d",&b,&td);
60 add_edge(i+1,b,1-td*0.01);
61 }
62 }
63 scanf("%d%d%d",&a,&b,&td);
64 spfa(a);
65 if(d[b]==0){
66 printf("IMPOSSIBLE!\n");
67 }else{
68 printf("%.2f\n",td-td*d[b]);
69 }
70 }
71 return 0;
72 }
View Code
H.hdu4302
解题关键:优先队列分别维护左右距离x最近的点,然后模拟即可。
1 #include<bits/stdc++.h>
2 using namespace std;
3 typedef long long ll;
4 priority_queue<int>q1;
5 priority_queue<int,vector<int>,greater<int> >q2;
6 int main(){
7 int t,l,n,a,b,now,x,m,ans;
8 scanf("%d",&t);
9 for(int ca=1;ca<=t;ca++){
10 x=0;
11 scanf("%d%d",&n,&m);
12 while(!q1.empty()) q1.pop();
13 while(!q2.empty()) q2.pop();
14 ans=0;
15 for(int i=0;i<m;i++){
16 scanf("%d",&a);
17 if(a==1){
18 if(!q1.empty()&&!q2.empty()){
19 int t1=x-q1.top();
20 int t2=q2.top()-x;
21 if(t1<t2){
22 x=q1.top();
23 now=-1;
24 ans+=t1;
25 q1.pop();
26 }
27 else if(t1>t2){
28 x=q2.top();
29 now=1;
30 q2.pop();
31 ans+=t2;
32 }
33 else if(now==1){
34 x=q2.top();
35 now=1;
36 q2.pop();
37 ans+=t2;
38 }else{
39 x=q1.top();
40 now=-1;
41 q1.pop();
42 ans+=t1;
43 }
44 }
45 else if(!q1.empty()){
46 int t1=x-q1.top();
47 now=-1;
48 x=q1.top();
49 ans+=t1;
50 q1.pop();
51 }
52 else if(!q2.empty()){
53 int t2=q2.top()-x;
54 now=1;
55 x=q2.top();
56 q2.pop();
57 ans+=t2;
58 }
59 }else{
60 scanf("%d",&b);
61 if(b>x){
62 q2.push(b);
63 }else{
64 q1.push(b);
65 }
66 }
67 }
68 printf("Case %d: %d\n",ca,ans);
69 }
70 }
View Code
I.hdu4308
解题关键:bfs简单裸题,考验手速和细节。
1 #include<bits/stdc++.h>
2 #define inf 0x3f3f3f3f
3 using namespace std;
4 typedef long long ll;
5 char arr[5002][5002];
6 int r,c,cost,sx,sy,tx,ty;
7 struct node{
8 int x,y;
9 };
10 int d[5002][5002];
11 const int dx[4]={1,0,-1,0};
12 const int dy[4]={0,1,0,-1};
13 node q1[5002];int rear;
14 int bfs(node st){
15 for(int i=0;i<r;i++) for(int j=0;j<c;j++) d[i][j]=inf;
16 queue<node>q;
17 q.push(st);
18 d[st.x][st.y]=0;
19 while(q.size()){
20 node p=q.front();
21 q.pop();
22 //if(p.x==tx&&p.y==ty) return d[p.x][p.y];
23 for(int i=0;i<4;i++){
24 int nx=p.x+dx[i],ny=p.y+dy[i];
25 if(nx<0||nx>=r||ny<0||ny>=c||arr[nx][ny]=='#'||d[nx][ny]!=inf) continue;
26 if(arr[nx][ny]=='*'){
27 q.push((node){nx,ny});
28 d[nx][ny]=d[p.x][p.y]+1;
29 }else if(arr[nx][ny]=='P'){
30 d[nx][ny]=d[p.x][p.y];
31 for(int i=0;i<rear;i++){
32 int tmpx=q1[i].x,tmpy=q1[i].y;
33 d[tmpx][tmpy]=d[nx][ny];
34 q.push((node){tmpx,tmpy});
35 }
36 }else if(arr[nx][ny]=='C'){
37 d[nx][ny]=d[p.x][p.y];
38 return d[nx][ny];
39 }
40 }
41 }
42 return -1;
43 }
44 int main(){
45 while(scanf("%d%d%d",&r,&c,&cost)!=EOF){
46 rear=0;
47 for(int i=0;i<r;i++) scanf("%s",arr+i);
48 for(int i=0;i<r;i++) for(int j=0;j<c;j++){
49 if(arr[i][j]=='Y') sx=i,sy=j;
50 if(arr[i][j]=='C') tx=i,ty=j;
51 if(arr[i][j]=='P'){
52 q1[rear++]=(node){i,j};
53 }
54 }
55 int ans=bfs((node){sx,sy});
56 if(ans==-1) printf("Damn teoy!\n");
57 else printf("%d\n",ans*cost);
58 }
59 return 0;
60 }
View Code
J.UVa1521
解题关键:贪心,会得出猜的数是1时需要的步数最大,然后就是将n以内的质数全部筛出,转化为质数分组的问题,然后双指针解决即可。
每次询问,相当于确定每个质数P是否出现。考虑到最坏情况,一定是问什么都回答1,否则的话就相当于把$n$缩小成了$\frac{n}{p}$。
问题就转换成了将1~n中间的质数分成K组,每组内的质数之积不大于$n$,求最小的$k$。
对于不小于$\sqrt{n}$的质数来说,他一定只能和小于$\sqrt{n}$的质数来配对,这样答案至少为不小于$\sqrt{n}$的质数个数。
而小于$\sqrt{n}$的质数的个数一定比不小于$\sqrt{n}$的质数的个数小很多,并且一定可以找到一个质数来配对。
所以问题的答案可以通过贪心来求,递减枚举每一个不小于质数,判断当前最小质数是否可以合并。
1、考虑乘法,小的因子每增大1,大的因子势必会减小很多,利用这个性质,可以得到,每个小的因子只能和一个大的因子合并。
1 #include<bits/stdc++.h>
2 using namespace std;
3 typedef long long ll;
4 const int MAXN=30001;
5 int prime[MAXN];//保存素数
6 bool vis[MAXN];//初始化
7 int LSsieve(int n){
8 int cnt=0;
9 memset(vis,0,sizeof vis);
10 for(int i=2;i<n;i++){
11 if(!vis[i]) prime[cnt++]=i;
12 for(int j=0;j<cnt&&i*prime[j]<n;j++){
13 vis[i*prime[j]]=1;
14 if(i%prime[j]==0) break;
15 }
16 }
17 return cnt;//返回小于n的素数的个数
18 }
19
20
21 int main(){
22 int n;
23 int cnt=LSsieve(10001);
24 while(scanf("%d",&n)!=EOF){
25 int i=0,j=cnt-1,ans=0;
26 while(prime[j]>n) j--;
27 while(i<=j){
28 ans++;
29 if(prime[i]*prime[j]<=n) i++,j--;
30 else j--;
31 }
32 printf("%d\n",ans);
33 }
34
35 return 0;
36 }
View Code
2、暴力分组。先取最大,然后用小的填充。
1 #include<cstdio>
2 #include<cstring>
3 #include<algorithm>
4 #include<cstdlib>
5 #include<iostream>
6 #include<cmath>
7 using namespace std;
8 typedef long long ll;
9 const int MAXN=30001;
10 int prime[MAXN];//保存素数
11 bool vis[MAXN];//初始化
12 int LSsieve(int n){
13 int cnt=0;
14 memset(vis,0,sizeof vis);
15 for(int i=2;i<n;i++){
16 if(!vis[i]) prime[cnt++]=i;
17 for(int j=0;j<cnt&&i*prime[j]<n;j++){
18 vis[i*prime[j]]=1;
19 if(i%prime[j]==0) break;
20 }
21 }
22 return cnt;//返回小于n的素数的个数
23 }
24
25
26 int main(){
27 int n;
28 int cnt=LSsieve(10001);
29 while(scanf("%d",&n)!=EOF){
30 int i=0,j=cnt-1,ans=0,now;
31 while(prime[j]>n) j--;
32 while(i<=j){
33 now=prime[j];
34 while(prime[i]*now<=n) now*=prime[i],i++;
35 j--;
36 ans++;
37 }
38 printf("%d\n",ans);
39 }
40 return 0;
41 }
View Code