西电10.9校内赛补题
C.
http://www.cnblogs.com/elpsycongroo/p/7643771.html
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 }
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 }
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 }
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 }
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 }
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 }
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 }
J.UVa1521
解题关键:贪心,会得出猜的数是1时需要的步数最大,然后就是将n以内的质数全部筛出,转化为质数分组的问题,然后双指针解决即可。
每次询问,相当于确定每个质数P是否出现。考虑到最坏情况,一定是问什么都回答1,否则的话就相当于把$n$缩小成了$\frac{n}{p}$。
问题就转换成了将1~n中间的质数分成K组,每组内的质数之积不大于$n$,求最小的$k$。
而小于$\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 }
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 }