1009
直接贪心。类似于SPFA
算法。我们要求损耗最小,也就是剩余最大。对于每个节点,我们记录起当前可以达到的剩余最大电力。和 Dijkstra算法相似,我们这里每次找寻的是尚未标记的拥有最大值的结点,并把这个最大值作为当前结点的最终结果,标记此结点并通过当前结点拓展与之 相连的结点。因为从一个结点传输电力到另一个几点,电力的总量是不会增加的。所以,在以后的贪心过程中,不会更新之前已经标记的结点,因为不可能有更大的 值。
这样只要求得最后到达t的最大剩余电力就能得出答案。
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<set> #include<map> #include<queue> #include<vector> #include<string> #define Min(a,b) a<b?a:b #define Max(a,b) a>b?a:b #define CL(a,num) memset(a,num,sizeof(num)); #define maxn 50200 #define inf 9999999 using namespace std; struct node { int k; int cost; node(int x,int y):k(x),cost(y){} }; vector<node>p[maxn]; struct cnode { int k; double w; cnode (int x,double y ) :k(x),w(y){} }; queue<cnode>que; int s,t,n; double ans; int vis[maxn]; double pow; double f[maxn]; void init() { for(int i=0;i<=n;i++) { p[i].clear(); vis[i]=0; f[i]=-1; } ans=-1; } void SPFA() { int i; while(!que.empty())que.pop(); que.push(cnode(s,pow)); vis[s]=1; f[s]=pow; while(!que.empty()) { cnode a=que.front();que.pop(); int k=a.k; double w=a.w; vis[k]=0; for(i=0;i<p[k].size();i++) { node b=p[k][i]; double t=f[k] - f[k]*b.cost/100.0; if(t>f[b.k]) { f[b.k]=t; if(!vis[b.k]) { que.push(cnode(b.k,f[b.k])) ; vis[b.k]=1; } } } } } int main() { //freopen("in.txt","r",stdin); int i,a,x,y; while(scanf("%d",&n)!=EOF) { init(); for(i=1;i<=n;i++) { scanf("%d",&a); while(a--) { scanf("%d%d",&x,&y); p[i].push_back(node(x,y)); } } scanf("%d%d%lf",&s,&t,&pow); BFS(); if(f[t]==-1)printf("IMPOSSIBLE!\n"); else printf("%.2lf\n",pow-f[t]); } }
4310 Hero
http://acm.hdu.edu.cn/showproblem.php?pid=4310
此题官方的解题报告是 状态压缩dp,不过贪心 也能过
今天用 状态压缩dp 做了一下,对状态压缩 dp 有了进一步的了解
有转移方程dp[mask] = min{dp[mask - {i}] + hp_sum[mask] * dps[i], for all i in mask}
1 #include<stdio.h> 2 #include<iostream> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 #include<queue> 7 #include<set> 8 #include<map> 9 #define Min(a,b) a>b?b:a 10 #define Max(a,b) a>b?a:b 11 #define CL(a,num) memset(a,num,sizeof(a)); 12 #define inf 9999999 13 #define maxn 200000 14 using namespace std; 15 int dp[1<<20],sum[1<<20]; 16 struct node 17 { 18 int dps; 19 int hp; 20 }p[30]; 21 int main() 22 { 23 int n,i,s; 24 while(scanf("%d",&n)!=EOF) 25 { 26 for(i=0;i<n;i++) 27 scanf("%d%d",&p[i].dps,&p[i].hp); 28 CL(sum,0); 29 for(s=0;s<(1<<n);s++) 30 { 31 for(i=0;i<n;i++) 32 { 33 if(s&(1<<i))sum[s]+=p[i].hp; 34 } 35 } 36 for(s=0;s<1<<n;s++) 37 dp[s]=inf; 38 39 40 dp[0]=0; 41 for(s=0;s<(1<<n);s++) 42 { 43 for(i=0;i<n;i++) 44 { 45 if(s&(1<<i))continue; 46 int t=1<<i; 47 dp[s+t]=Min(dp[s+t],dp[s]+sum[s+t]*p[i].dps); 48 } 49 } 50 int k=1<<n; 51 cout<<dp[k-1]<<endl; 52 } 53 }
4311
Meeting point-1
题意:在二维图中 给出n个点的坐标,求出,其中一点使得 其他点到达 这个距离最短(只能上下左右的走)
官方题解:
Meeting point-1:
平面上两点间的 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 所需要的总时间。
#include<cstdio> #include<queue> #include<iostream> #include<vector> #include<map> #include<algorithm> #include<cmath> #define Min(a,b) a<b?a:b #define maxn 200000 #define inf 0x7fffffff typedef long long ll; using namespace std; struct Point{ int x, y; Point(){} Point(int _x, int _y):x(_x),y(_y){} }ary[maxn]; ll sx[maxn],sy[maxn],x[maxn],y[maxn]; int main() { int T, n; cin>>T; while(T--){ scanf("%d",&n); for(int i = 0; i < n; ++i){ cin>>x[i]>>y[i]; ary[i]=Point(x[i],y[i]); } sort(x, x + n); sort(y, y + n); sx[0] = x[0]; sy[0] = y[0]; for(int i = 1; i < n; ++i){ sx[i] = sx[i - 1] + x[i]; sy[i] = sy[i - 1] + y[i]; } ll ans=1,sumx,sumy; ans<<=60; for(int i=0;i<n;i++) { int rankx = lower_bound(x,x+n,ary[i].x)-x;//求出数组x中比ary[i].x小的数 int ranky = lower_bound(y,y+n,ary[i].y)-y; sumx = ll(ary[i].x)*(rankx)-sx[rankx-1]+sx[n-1]-sx[rankx]-ll(ary[i].x)*(n-1-rankx); sumy = ll(ary[i].y)*(ranky)-sy[ranky-1]+sy[n-1]-sy[ranky]-ll(ary[i].y)*(n-1-ranky); ans=Min(ans,sumx+sumy); } cout<<ans<<endl; } }
1004
题意 :
给出一个图,每一条边都有权值,给定k个点删除 k-1条边是 这 k个点不联通,
求删除的边的和权值最小
题解:
kuskal 建生成树的过程,只不过将 边按从大到小牌,(因为 先将大边 插入到树中
这样保证我们删边时 ,删的是最小的,(如果是按从小的大牌的话,我们选中的就是最大的 将两个要删的联通的边
因为 我们这样,是先用的小边 建树)
比赛是没想到,看到题解,擦涣然大悟 汗。。。。。。)
没加入一条边时判断 是否将,俩个给定的点联通,如果是,则将这条边删除
开始想到 判断是否将两个给定的点联通,难道要 遍历一下吗,那么大的数据两
后来想到 ,我么可以将 给定的点作为 集合的表,这样就可以在 O(1)的时间内判断了
1 #include<stdio.h> 2 #include<iostream> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 #include<queue> 7 #include<set> 8 #include<map> 9 #define Min(a,b) a>b?b:a 10 #define Max(a,b) a>b?a:b 11 #define CL(a,num) memset(a,num,sizeof(a)); 12 #define maxn 200000 13 using namespace std; 14 struct node 15 { 16 int x; 17 int y; 18 int cost; 19 }p[maxn]; 20 int f[maxn],vis[maxn]; 21 int find(int x) 22 { 23 if(f[x]!=x)f[x]=find(f[x]); 24 return f[x]; 25 } 26 int cmp( const void *a ,const void *b) 27 { 28 return (*(node *)a).cost < (*(node *)b).cost ? 1 : -1; 29 } 30 int main() 31 { 32 freopen("in.txt","r",stdin); 33 int t,n,k,i,a; 34 scanf("%d",&t); 35 while(t--) 36 { 37 scanf("%d%d",&n,&k); 38 for(i=0;i<=n;i++)f[i]=i; 39 for(i=0;i<n-1;i++) 40 { 41 scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].cost); 42 43 } 44 45 CL(vis,0); 46 for(i=0;i<k;i++) 47 { 48 scanf("%d",&a); 49 vis[a]=1; 50 } 51 qsort(p,n-1,sizeof(p[0]),cmp); 52 long long ans=0; 53 for(i=0;i<n-1;i++) 54 { 55 int x=find(p[i].x); 56 int y=find(p[i].y); 57 if(vis[x]&&vis[y])ans+=p[i].cost; 58 else 59 { 60 61 if(vis[x]) 62 f[y]=x; 63 else 64 if(vis[y])f[x]=y; 65 else f[x]=y; 66 67 } 68 } 69 cout<<ans<<endl; 70 } 71 }