http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3450
http://acm.hdu.edu.cn/showproblem.php?pid=4345
http://acm.hdu.edu.cn/showproblem.php?pid=4341
zoj 3450 和 hdu 4341 是同样的题,- -!,真不知道多校审题是怎么审的
可以直接极角排序,对同一条线上的点处理一下,
即要么区第一个,要么取第一二两个。。。。
一条线就是一组物品,每组物品最多选一个,典型的分组背包,如果用一维数组的话要小心物品的体积为0的情况。
zoj 3450
这题极角排序的话好像中间计算结果会溢出,用double可能会有浮点误差,所以直接用数学方法做了
#include<cstdio> #include<cstring> #include<vector> #include<cstdlib> #include<map> #include<cmath> #include<algorithm> using namespace std; const int N_MAX = 510; int dp[10010]; int gcd(int a,int b){ return !b ? a : gcd(b,a%b); } int main() { int T,n; int x,y,z,t,val,x0,y0; while(scanf("%d%d%d%d",&x0,&y0,&n,&T)!=EOF) { map<pair<int,int>,vector<pair<int,pair<int,int> > > > mp; for(int i=0;i<n;i++) { scanf("%d%d%d%d",&x,&y,&t,&val); x-=x0;y-=y0; int z=gcd(abs(x),abs(y)); x/=z;y/=z; mp[make_pair(x,y)].push_back(make_pair(z,make_pair(t,val))); } vector<pair<int,int> > edge[N_MAX]; int tot=0; for( map<pair<int,int>,vector<pair<int,pair<int,int> > > >::iterator it=mp.begin();it!=mp.end();it++) { sort(it->second.begin(),it->second.end()); int sum1=0,sum2=0; for(vector<pair<int,pair<int,int> > >::iterator i = it->second.begin();i!=it->second.end();i++) { sum1+=i->second.first; sum2+=i->second.second; edge[tot].push_back(make_pair(sum1,sum2)); } tot++; } fill(dp,dp+T+1,0); for(int i=0;i<tot;i++) { for(int j=T;j>=0;j--) { int sz=edge[i].size(); int tmp=dp[j];//体积可能会等于0,所以要用一个临时变量记录最优值 for(int k=0;k<sz;k++) { if(j>=edge[i][k].first) { tmp=max(tmp,dp[j-edge[i][k].first]+edge[i][k].second);//否则会导致 dp[j]=max(dp[j],dp[j-0]+second),自己更新自己 } } dp[j]=tmp; } } printf("%d\n",dp[T]); } return 0; }
#include<cstdio> #include<cstring> #include<vector> #include<algorithm> using namespace std; struct Point{ int x,y,t,val; Point(int _x,int _y):x(_x),y(_y){} Point(){} }in[210]; int dp[40010]; Point cent; inline int det(Point a,Point b,Point c){ return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x); } int dis(Point a,Point b){ return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y); } int cmp(Point a,Point b){ if( det(cent,a,b)==0) return dis(a,cent)<dis(b,cent); return det(cent,a,b)>0; } int main(){ int n,T,ca=1; while(scanf("%d%d",&n,&T)!=EOF){ vector<pair<int,int> > edge[210]; for(int i=0;i<n;i++) scanf("%d%d%d%d",&in[i].x,&in[i].y,&in[i].t,&in[i].val); cent=Point(0,0); sort(in,in+n,cmp); int tot=0; for(int i = 0, j ; i < n ; i = j){ int sum1=in[i].t,sum2=in[i].val; edge[tot].push_back(make_pair(sum1,sum2)); for(j = i+1; det(cent,in[j],in[i]) == 0 && j < n; j++){ sum1 += in[j].t,sum2 += in[j].val; edge[tot].push_back(make_pair(sum1,sum2)); } tot++; } memset(dp,0,sizeof(dp)); for(int i=0;i<tot;i++) for(int v=T;v>=0;v--) for(int j=0;j<edge[i].size();j++) if(v>=edge[i][j].first) dp[v]=max(dp[v],dp[v-edge[i][j].first]+edge[i][j].second); printf("Case %d: %d\n",ca++,dp[T]); } return 0; }
hdu 4345
将一个数分成若干份,求这若干份数的不同的最小公倍数的总数
注意 到每个数最终都可以分解为若干质数比如4 * 5 就相当于2^2 * 5,所以就可以分组了
2 2^2 2^3 2^4 2^5 .....
3 3^2 3^3 3^4 3^5.......
.....
每一组最多选一个物品,最小公倍数肯定不会重复(因为都是质数的不同组合)
dp[i]表示各个数的和小于等于i时 总的最小公倍数的个数,因为dp[0]=1,用0的容量装体积为0,所以dp[1~V]=1
最后的答案是dp[V]
而如果状态定义为各个数的和恰好为i时的总最小公倍数的个数dp[0]=1,dp[1~V]=0;
最后的答案是sigma(dp[i])
#include<cstdio> #include<cstring> #include<vector> #include<cmath> #include<algorithm> using namespace std; __int64 dp[1010]; vector<int> edge[200]; bool isp(int num){ for(int i=2;i<=sqrt(num*1.0);i++){ if(num%i==0) return false; } return true; } int main(){ int n; while(scanf("%d",&n)!=EOF){ int tot=0; for(int i=0;i<200;i++) edge[i].clear(); for(int i=2;i<1000;i++){ if(isp(i)){ for(int j=i;j<=1000;j*=i){ edge[tot].push_back(j); } tot++; } } for(int i=0;i<=n;i++) dp[i]=1; for(int i=0;i<tot;i++) for(int v=n;v>=0;v--) for(int j=0;j<edge[i].size()&&edge[i][j]<=v;j++) dp[v]+=dp[v-edge[i][j]]; printf("%I64d\n",dp[n]); } return 0; }