A Supermarket POJ - 1456
题意:有n种物品,给出每个物品的价格和保质期,每天只能卖出一个未过期的物品,求商家最大能获利多少钱?
思路:有两种方法可以用贪心写,也可以用并查集写。
贪心代码:
#include<stdio.h> #include<string.h> #include<math.h> #include<queue> #include<stdlib.h> #include<map> #include<vector> #include<algorithm> #include<iostream> using namespace std; #define mem(a,b) memset(a,b,sizeof(a)) #define lowbit(x) x&(-x) #define ll long long #define inf 0x3f3f3f3f #define mod 1000000007 #define eps 1e-9 #define PI acos(-1.0) #define N 110000 struct node { int x,y; } q[N]; bool cmp(node a,node b) { if(a.x==b.x) return a.y<b.y; return a.x>b.x; } int n,sum,t,book[N]; int main() { while(~scanf("%d",&n)) { mem(book,0); for(int i=1; i<=n; i++) scanf("%d%d",&q[i].x,&q[i].y); sort(q+1,q+1+n,cmp); sum=0,book[0]=1; for(int i=1; i<=n; i++) { if(!book[q[i].y]) { sum+=q[i].x; book[q[i].y]=1; } else { while(q[i].y--) { if(!book[q[i].y]) { sum+=q[i].x; book[q[i].y]=1; break; } } } } printf("%d\n",sum); } }
并查集代码:
#include<stdio.h> #include<string.h> #include<math.h> #include<vector> #include<queue> #include<algorithm> using namespace std; typedef long long ll; #define mem(a,b) memset(a,b,sizeof(a)) #define N 10010 int F[N],n; struct node { int p,d; } q[N]; bool cmp(node a,node b)//按p从大到小排序。d没有关系 { return a.p>b.p; } int getf(int x) { if(F[x]==-1) return x; return F[x]=getf(F[x]); } int main() { while(~scanf("%d",&n)) { mem(F,-1); for(int i=0; i<n; i++) scanf("%d%d",&q[i].p,&q[i].d); sort(q,q+n,cmp); int ans=0; for(int i=0; i<n; i++) { int t=getf(q[i].d); if(t>0) { ans+=q[i].p; F[t]=t-1; } } printf("%d\n",ans); } }
B Is It A Tree? POJ - 1308
题意:给出多组边,让你判断这是不是一棵树。
思路:每输入两个点都判断一下,这两个点连接以后会不会成为环,如果成环就不符合树的定义了,所有数合并之后,遍历一遍所有输入的数,看看他们是不是都有一个共同的祖先,否则那也不是树。(主要是数据处理比较麻烦)
AC代码:
#include<string.h> #include<stdio.h> #include<algorithm> using namespace std; int f[1200000],a[1200000],b[1100000],book[1200000],ww; int getf(int v) { if(f[v]==v) return v; else return f[v]=getf(f[v]); } void merge(int x,int y) { int t1,t2; t1=getf(x); t2=getf(y); if(t1!=t2) f[t2]=t1; else ww++; } int main() { int kk=1,aaa,bbb; while(~scanf("%d%d",&aaa,&bbb)&&(aaa!=-1||bbb!=-1)) { if(aaa==0&&bbb==0)///进行数据处理 { printf("Case %d ",kk++); printf("is a tree.\n"); continue; } int maxx=-1,ans=0; memset(f,0x3f3f3f3f,sizeof(f)); memset(book,0,sizeof(book)); if(book[aaa]==0) { book[aaa]=1; ans++; } if(book[bbb]==0) { book[bbb]=1; ans++; } maxx=max(maxx,max(aaa,bbb)); f[aaa]=aaa; f[bbb]=bbb; a[0]=aaa; b[0]=bbb; int t=1,aa,bb; while(scanf("%d%d",&aa,&bb)&&(aa||bb)) { maxx=max(maxx,max(aa,bb)); if(book[aa]==0) { book[aa]=1; ans++; } if(book[bb]==0) { book[bb]=1; ans++; } f[aa]=aa; f[bb]=bb; a[t]=aa; b[t]=bb; t=t+1; } ww=0; int sum=0; for(int i=0; i<t; i++) merge(a[i],b[i]); for(int i=0; i<=maxx; i++) { if(f[i]==0x3f3f3f3f) continue; if(f[i]==i) sum++; } printf("Case %d ",kk++); if(sum==1&&ans-1==t&&ww==0) printf("is a tree.\n"); else printf("is not a tree.\n"); } }
C The Accomodation of Students HDU - 2444
题意:有n个学生,有m对人是认识的,每一对认识的人可以分到一个房间,问能否把n个学生分成两部分,每部分内的学生互不认识,而两部分之间的学生认识。如果可以分成两部分,就算出房间最多需要多少间,否则就输出No。
思路:判断是否为二分图,然后判断最大匹配。
AC代码:
#include<stdio.h> #include<string.h> #include<queue> #include<map> #include<vector> #include<iostream> #include<math.h> #include<stdlib.h> #include<algorithm> using namespace std; #define eps 1e-8 #define mod 1000000007 #define lowbit(x) x&(-x) #define ll long long #define mem(a,b) memset(a,b,sizeof(a)) #define PI acos(-1.0) #define inf 0x3f3f3f3f #define N 210 int n,m,book[N],vis[N]; int a,b,s[N][N]; int bfs(int x) { queue<int>q; q.push(1); book[1]=1; while(!q.empty()) { int u=q.front(); q.pop(); for(int i=1; i<=n; i++) { if(book[i]==0&&s[u][i]) { book[i]=-book[u]; q.push(i); } else if(book[i]==book[u]&&s[u][i]) return 0; } } return 1; } int find1(int x) { for(int i=1; i<=n; i++) if(vis[i]==0&&s[x][i]) { vis[i]=1; if(book[i]==-1||find1(book[i])) { book[i]=x; return 1; } } } int main() { while(~scanf("%d%d",&n,&m)) { mem(s,0),mem(book,0); for(int i=1; i<=m; i++) scanf("%d%d",&a,&b),s[a][b]=1; int flag=bfs(1); if(!flag) printf("No\n"); else { int sum=0; mem(book,-1); for(int i=1; i<=n; i++) { mem(vis,0); if(find1(i)) sum++; } printf("%d\n",sum); } } }
D 畅通工程再续 HDU - 1875
题意:中文题,不在解释了
思路:只有当前岛屿和其他的距离在10到1000这个范围内就可以相连到一块,我们可以把这些相连的岛屿存放到结构体中,然后跑一遍最小生成树就可以了。
AC代码:
#include<stdio.h> #include<string.h> #include<stdlib.h> #include<algorithm> #include<queue> #include<math.h> #include<vector> #include<map> #include<iostream> using namespace std; #define PI acos(-1.0) #define ll long long #define eps 1e-8 #define mem(a,b) memset(a,b,sizeof(a)) #define mod 1000000007 #define inf 0x3f3f3f3f #define N 110 struct node { int x,y; double dis; } q[50000]; int t; int n,m; int f[N]; int a[N],b[N]; bool zmh(node a,node b){return a.dis<b.dis;} int getf(int v) { if(f[v]==v) return v; else return f[v]=getf(f[v]); } void kruskal(int cnt) { int num=0; double ans=0.0; for(int i=0; i<cnt; i++) { int t1=getf(q[i].x); int t2=getf(q[i].y); if(t1!=t2&&q[i].dis>=10&&q[i].dis<=1000){ f[t1]=t2; num++; ans+=q[i].dis; } if(num==n-1||q[i].dis>1000)break; } if(num==n-1)printf("%.1f\n",ans*100); else printf("oh!\n"); return ; } int main() { scanf("%d",&t); while(t--) { mem(f,0); scanf("%d",&n); for(int i=1; i<=n; i++) { scanf("%d%d",&a[i],&b[i]); f[i]=i; } int k=0; for(int i=1; i<=n; i++) for(int j=i+1; j<=n; j++) { q[k].x=i; q[k].y=j; q[k++].dis=sqrt(1.0*(a[j]-a[i])*(a[j]-a[i])+1.0*(b[j]-b[i])*(b[j]-b[i]))*1.0; } sort(q,q+k,zmh); kruskal(k); } }
E Invitation Cards POJ - 1511
题意:有n个车站,m条公交车路线,每条公交车路线都有一个价格。要求从起始站到每一站,然后再从每一站回到起始站,求最少花的钱数。(注意是m条单向边)
思路:用spfa算法正向建边求出一个dis1 [ i ] 的数组,表示起始站到 i 站的最少花费,再用spfa算法反向建边求出一个dis2 [ i ] 的数组,表示 i 站到起始站的最少花费,然后把两个数组对应相加求和就是最终结果。
AC代码:
#include<stdio.h> #include<string.h> #include<queue> #include<algorithm> using namespace std; int first[1100000],next[1100000],u[1100000],v[1010000],w[1100000]; int book[1100000],dis1[1100000],dis2[1100000]; int main() { int t; scanf("%d",&t); while(t--) { int n,m; scanf("%d%d",&n,&m); int tt=0; memset(first,-1,sizeof(first)); for(int i=1; i<=m; i++) { scanf("%d%d%d",&u[i],&v[i],&w[i]); next[i]=first[u[i]];///正向建边 first[u[i]]=i; } memset(book,0,sizeof(book)); memset(dis1,0x3f3f3f3f,sizeof(dis1)); memset(dis2,0x3f3f3f3f,sizeof(dis2)); queue<int>q; q.push(1); dis1[1]=0; while(!q.empty())///spfa算法 { int x=q.front(); q.pop(); book[x]=0; for(int j=first[x]; j!=-1; j=next[j]) { if(dis1[v[j]]>dis1[x]+w[j]) { dis1[v[j]]=dis1[x]+w[j]; if(book[v[j]]==0) { book[v[j]]=1; q.push(v[j]); } } } } memset(book,0,sizeof(book)); memset(first,-1,sizeof(first)); for(int i=1; i<=m; i++)///反向建边 { next[i]=first[v[i]]; first[v[i]]=i; } queue<int>Q; Q.push(1); dis2[1]=0; while(!Q.empty())///spfa算法 { int x=Q.front(); Q.pop(); book[x]=0; for(int j=first[x]; j!=-1; j=next[j]) { if(dis2[u[j]]>dis2[x]+w[j]) { dis2[u[j]]=dis2[x]+w[j]; if(book[u[j]]==0) { book[u[j]]=1; Q.push(u[j]); } } } } long long sum=0; for(int i=1; i<=n; i++)///对应相加求和 sum=sum+dis1[i]+dis2[i]; printf("%lld\n",sum); } return 0; }
F Invade the Mars HDU - 3873
题意:就是美国要从 1 攻打火星中心N(就是求从1到N的最短距离),但是会给一些 A 城市被 B 城市保护,要想攻打A 城市就必须先攻打 B 城市。
思路:(最短路径+Dijkstra(稍微有点修改))用num[i]标记第i个城市被保护的数目,只有当该点被保护的数目为0时,才能入S集合,从而优化到其他点的时间。当前进入S集合的城市,遍历它所保护的城市,num[i]减一,记录下被保护的城市解除保护所需的最长时间。说的有点绕,看代码会比较清楚。
AC代码:
#include<stdio.h> #include<string.h> #include<vector> #include<iostream> #define N 70005 #include<algorithm> using namespace std; int book[3005];//标记这个城市是否遍历过 int root[3005];//例root[i]表示起点到i城市的最远距离,i是被保护的城市。 int num[3005];//记录城市i被多少个城市保护 int e[3005][3005];//两城市之间的最短距离 int dis[3005];//起点到i之间的最短距离 vector<int>v[3005];//例如v[u][i],表示u城市保护i城市 int main() { int t; scanf("%d",&t); while(t--) { int n,m,a,b,c,h; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)//容器清空 v[i].clear(); memset(root,0,sizeof(root)); memset(book,0,sizeof(book)); memset(e,0x3f3f3f3f,sizeof(e)); memset(dis,0x3f3f3f3f,sizeof(dis)); memset(num,0,sizeof(num)); for(int i=1; i<=m; i++) { scanf("%d%d%d",&a,&b,&c); e[a][b]=min(e[a][b],c); } for(int i=1; i<=n; i++) { scanf("%d",&num[i]); for(int j=1; j<=num[i]; j++) { scanf("%d",&h); v[h].push_back(i); } } dis[1]=0; for(int i=1; i<=n; i++) { int minn=0x3f3f3f3f,u;//u是保护的城市 for(int j=1; j<=n; j++) { dis[j]=max(dis[j],root[j]);//每遍历一个点都要更新一下 if(dis[j]<minn&&book[j]==0&&num[j]==0) { u=j; minn=dis[j]; } } if(minn==0x3f3f3f3f) break; book[u]=1; for(int j=0; j<v[u].size(); j++) { int k=v[u][j];//k是被保护的城市 num[k]--; root[k]=max(dis[u],root[k]);//选择最大的时间 } v[u].clear();//用过之后要清空 for(int j=1; j<=n; j++) if(dis[j]>dis[u]+e[u][j]) dis[j]=dis[u]+e[u][j]; } printf("%d\n",dis[n]); } return 0; }