题意:m*n的格子里放1*3的矩形,问有多少种放法。 分析:状态压缩dp,每个格子有三种状态,dp[i][j]表示到第i层状态为j的方法数。 #include <iostream> #include <memory.h> #include <stdio.h> using namespace std; #define LL long long
LL dp[31][20000],n,m,row,zt; LL num[10],pp[10];
// 状态 0----横放或者竖放的第三个格子 对下层没有影响 // 1----竖放的中间那个格子 对下一层有影响 // 2----竖放的第一个格子 对下两层有影响
void dfs(LL pos,LL status) { if(pos==n) { dp[row+1][status]+=dp[row][zt]; return; } if(num[pos]==0) // 上层为0 这层可以横放或者成2 { if(pos+2<n&&num[pos+1]==0&&num[pos+2]==0) dfs(pos+3,status); // 横放 dfs(pos+1,status+2*pp[pos]); } else if(num[pos]==1) // 上层为1 这层为0 { dfs(pos+1,status); } else // 上层为2 这层为1 { dfs(pos+1,status+pp[pos]); } }
void change() { LL now=zt,k=-1; memset(num,0,sizeof(num)); if(now==0) return; while(now) { num[++k]=now%3; now/=3; } }
int main() { LL i,nmax; pp[0]=1; for(i=1;i<=9;i++) pp[i]=pp[i-1]*3; while(scanf("%lld%lld",&n,&m)&&(m||n)) { if((m*n)%3!=0) {printf("0\n"); continue;} nmax=pp[n]; // 状态总数 memset(dp,0,sizeof(dp)); dp[0][0]=1; for(row=0;row<=m-1;row++) { for(zt=0;zt<nmax;zt++) { if(dp[row][zt]) { change(); dfs(0,0); } } } printf("%lld\n",dp[m][0]); } return 0; }
题意: ...... 分析: 二分答案,对于当前二分到的答案,验证在这个时间内是否可以完成,dp[i][j]表示到第i个人时,完成了j个A任务时最多能完成B任务的个数。然后枚举第i个人完成A任务的个数k来转移,dp[i][j]=max{dp[i-1][j-k]+第i个人完成k个A任务后还能完成B任务的个数}。 #include <iostream> #include <stdio.h> #include <memory.h> using namespace std;
int dp[110],a[110],b[110],maxx,n,m;
int judge(int now) { int i,j,k,tem; memset(dp,-1,sizeof(dp)); for(j=0;j<=m;j++) { if(j*a[1]>now) break; tem=now-j*a[1]; // 第一个人完成j个任务1后还剩的时间 tem=tem/b[1]; // 第一个人还能完成任务2的个数 dp[j]=tem; } for(i=2;i<=n;i++) { for(j=m;j>=0;j--) // 因为dp[]只用一维,所以类似完全背包 这里应该倒过来循环 { for(k=0;k<=j;k++) { if(k*a[i]>now) break; tem=now-k*a[i]; // 第i个人完成k个任务1后还剩的时间 tem=tem/b[i]; // 还能完成任务2的个数 if(dp[j-k]!=-1&&dp[j-k]+tem>dp[j]) dp[j]=dp[j-k]+tem; } } if(dp[m]>=m) return 1; } return 0; }
void solve() { int l=0,r=maxx*2*m,ans=r,mid; while(l<=r) { mid=(l+r)>>1; if(judge(mid)) { ans=mid; r=mid-1; } else l=mid+1; } printf("%d\n",ans); }
int main() { int t,i; scanf("%d",&t); while(t--) { scanf("%d%d",&n,&m); maxx=0; for(i=1;i<=n;i++) { scanf("%d%d",a+i,b+i); if(a[i]>maxx) maxx=a[i]; if(b[i]>maxx) maxx=b[i]; } solve(); } return 0; }
题意:n*m的网格,有些格子里可能有宝藏,若站在(x,y),每次只能向(x+1,y)或者(x,y+1)方向走,问最少要走几趟能将网格中的宝物收集完。 分析: 按题目的意思就是只能向右和向下走。如果将宝物的坐标按x轴来从小到大排(x轴相同的按y轴从小到大),那么就可以只考虑y坐标了 ,假设有两个位置i,j,i.x<=j.x,当i.y<=j.y时,才有可能收集完i后收集j,所以按x轴排序完后,问题就转化为用最少几个不降子序列可将y轴坐标序列全部涵盖。那就是最长(严格)递减子序列的长度了。
#include <iostream> #include <stdio.h> #include <memory.h> #include <algorithm> using namespace std;
struct node { int x,y; }a[100010];
int dp[100010],num[100010],b[100010],n,m,p;
int cmp(node p,node q) { if(p.x==q.x) return p.y<q.y; return p.x<q.x; }
void solve() // 求最长递减子序列 { int i,tem,l,r,mid,ans,top; b[1]=num[1]; top=1; dp[1]=1; ans=0; for(i=2;i<=p;i++) { if(num[i]<b[top]) { b[++top]=num[i]; dp[i]=top; } else { l=1; r=top; while(l<=r) { mid=(l+r)>>1; if(b[mid]<=num[i]) { tem=mid; r=mid-1; } else l=mid+1; } b[tem]=num[i]; dp[i]=tem; } if(dp[i]>ans) ans=dp[i]; } printf("%d\n",ans); }
int main() { int i; while(scanf("%d%d%d",&m,&n,&p)!=EOF) { for(i=1;i<=p;i++) scanf("%d%d",&a[i].x,&a[i].y); sort(a+1,a+1+p,cmp); for(i=1;i<=p;i++) num[i]=a[i].y; solve(); } return 0; }
摘要: 题意:有个人去一幢楼送礼物,他用两根柱状的容器装礼物,每次只能从容器的两端取出礼物送出去,上一层楼花费5的体力,下一层楼花费3的体力。求送完所有礼物耗费的最少体力。分析:不难的题目,只是目前写的人比较少而已。状态表示还是很好想出来的。dp[i1][j1][i2][j2][0]--第一根柱子到两端为i1,j1都送完,第二根到两端为i2,j2都送完,最后送的是a[i1]这个礼物所花费的最小体力。dp[... 阅读全文
题意: 将字符串压缩到最短。压缩规则见题目。 分析: 这题我就是用了最原始的方法了。一段一段来压缩。即现在要压缩[i,j]这一段时,要保证任意一段长度比j-i+1小的压缩结果都已经得出来了,也就是按长度从小到大来dp。压缩[i,j]的方法有两种,一种压缩成"数字(字符串)"的形式,另一种是压缩成[i,k][k+1,j]的形式。比较一下,哪种短就选哪种了。
#include <iostream> #include <stdio.h> #include <memory.h> using namespace std; #define inf 200000000
struct node { int len; char ss[110]; }f[110][110];
char ts[110]; int pos;
void tostring(int num) { char cc; int i; pos=-1; while(num) { ts[++pos]=(num%10)+'0'; num/=10; } for(i=0;i<=pos/2;i++) { cc=ts[i]; ts[i]=ts[pos-i]; ts[pos-i]=cc; } }
int main() { int i,j,k,nlen,now,p,q,n,num; char s[110]; while(scanf("%s",s+1)!=EOF) { n=strlen(s+1); for(i=1;i<=n;i++) // 初始化 { f[i][i].ss[0]=s[i]; f[i][i].ss[1]='\0'; f[i][i].len=1; } for(nlen=2;nlen<=n;nlen++) // 现在的长度 { for(i=1;i+nlen-1<=n;i++) { j=i+nlen-1; f[i][j].len=inf; // 先看能不能压缩成"数字(字符串)"的形式 for(now=1;now<nlen;now++) // now---想折叠成的长度 { if(nlen%now) continue; q=i+now; p=i; while(q<=j) { if(s[q]!=s[p]) break; p++; q++; if(p==i+now) p=i; } if(q>j) // 可以压缩成"数字(字符串)"的形式 { num=nlen/now; tostring(num); strcpy(f[i][j].ss,ts); f[i][j].ss[++pos]='('; for(p=0;f[i][i+now-1].ss[p];p++) f[i][j].ss[++pos]=f[i][i+now-1].ss[p]; f[i][j].ss[++pos]=')'; f[i][j].len=pos+1; f[i][j].ss[++pos]='\0'; break; } } for(k=i;k<j;k++) // [i,k][k+1,j]这种形式 { if(f[i][k].len+f[k+1][j].len<f[i][j].len) { f[i][j].len=f[i][k].len+f[k+1][j].len; strcpy(f[i][j].ss,f[i][k].ss); strcat(f[i][j].ss,f[k+1][j].ss); } } } } printf("%s\n",f[1][n].ss); } return 0; }
题意:当任意两个卫星的距离小于等于25时,放置一个天线就可以同时接受到这两个卫星的信号。现在告诉n个卫星的位置,求最少需购买天线数量,同时需要保证,得到的信号值总和最大。信号值计算公式是Qx=100-|卫星的位置-天线的位置|。 分析:只要弄清楚了题意,就很容易发现这个题其实就是经典的村庄里建邮局的问题了,只不过这里要求每一段的范围在25以内。因为数据范围只有100,就没写什么优化了,直接上n^3的。 f[i][j]表示前i个卫星放j个天线所能得到的最大信号。 f[i][j]=max{f[k][j-1]+w[k+1][i]; | d[i]-d[k+1]<=25}
#include <iostream> #include <stdio.h> #include <memory.h> #include <algorithm> using namespace std; #define eps 1e-8
int n; double f[110][110],w[110][110],d[110];
int main() { int i,j,k,tem; while(scanf("%d",&n)!=EOF) { d[0]=0.0; for(i=1;i<=n;i++) { scanf("%lf",&d[i]); } sort(d+1,d+n+1); for(i=1;i<=n;i++) // 预处理出任意一段放一个卫星的所获得的最大信号值。 { w[i][i]=100.0; for(j=i+1;j<=n;j++) { tem=i+(j-i+1)/2; w[i][j]=0.0; for(k=i;k<=tem;k++) w[i][j]+=d[tem]-d[k]; for(;k<=j;k++) w[i][j]+=d[k]-d[tem]; w[i][j]=(j-i+1)*100.0-w[i][j]; } } for(i=0;i<=n;i++) { for(j=0;j<=n;j++) f[i][j]=-1.0; } f[0][0]=0.0; f[1][1]=100.0; for(i=2;i<=n;i++) { for(j=1;j<=i;j++) { for(k=i-1;k>=j-1&&d[i]+eps<=25.0+d[k+1];k--) { if(f[k][j-1]+eps<0.0) continue; tem=i-k; if(f[k][j-1]+w[k+1][i]>f[i][j]+eps) f[i][j]=f[k][j-1]+w[k+1][i]; } } } for(j=1;j<=n;j++) { if(f[n][j]>=0) break; } printf("%d %.2lf\n",j,f[n][j]); } return 0; }
题意:好难表达... 分析:dp了,没什么好说的,dp[now]=min{dp[i]+dp[now-i],dp[i]+dp[now/i]且now%i==0,dp[i]+dp[j]且i^j==now;} 。不过转移的时候我的方法很暴力,不知道其他优秀的方法是怎么做的。 一些很明显的值的表达式可以预处理出来。
#include <iostream> #include <stdio.h> #include <memory.h> #include <cmath> using namespace std; #define inf 2000000000
struct node { int num; char s[100]; }a[20010];
void ini() { int i,now,j,tem,cnt; for(i=1;i<=20000;i++) a[i].num=inf; a[1].num=1; strcpy(a[1].s,"1"); // 1,2,3,5,7预处理出来 a[2].num=1; strcpy(a[2].s,"2"); a[3].num=1; strcpy(a[3].s,"3"); a[5].num=1; strcpy(a[5].s,"5"); a[7].num=1; strcpy(a[7].s,"7");
a[4].num=2; strcpy(a[4].s,"(2^2)"); // 4也预处理 a[6].num=1; strcpy(a[6].s,"3!"); // 预处理3到7的阶乘,因为8的阶乘已经超过20000了 a[24].num=2; strcpy(a[24].s,"(2^2)!"); // 注意阶乘的表达式外面就不用括号了,我一开始加了就wa a[120].num=1; strcpy(a[120].s,"5!"); a[720].num=1; strcpy(a[720].s,"3!!"); a[5040].num=1; strcpy(a[5040].s,"7!"); for(now=4;now<=20000;now++) // 下面就是dp了 转移的时候很暴力 { if(a[now].num!=inf) continue; tem=(int)sqrt(now+0.0); for(i=2;i<=tem;i++) { if(now%i==0) { j=now/i; if(a[i].num+a[j].num<a[now].num) // *运算 { a[now].num=a[i].num+a[j].num; strcpy(a[now].s,"("); strcat(a[now].s,a[i].s); strcat(a[now].s,"*"); strcat(a[now].s,a[j].s); strcat(a[now].s,")"); } cnt=now; j=0; while(cnt%i==0&&cnt>0) { ++j; cnt/=i; } if(cnt==1) { if(a[i].num+a[j].num<a[now].num) // ^运算 { a[now].num=a[i].num+a[j].num; strcpy(a[now].s,"("); strcat(a[now].s,a[i].s); strcat(a[now].s,"^"); strcat(a[now].s,a[j].s); strcat(a[now].s,")"); } } } } for(i=1;i<=now/2;i++) { j=now-i; if(a[i].num+a[j].num<a[now].num) // +运算 { a[now].num=a[i].num+a[j].num; strcpy(a[now].s,"("); strcat(a[now].s,a[i].s); strcat(a[now].s,"+"); strcat(a[now].s,a[j].s); strcat(a[now].s,")"); } } } }
int main() { ini(); int n; while(scanf("%d",&n)!=EOF) { printf("%s\n",a[n].s); } return 0; }
题意:给一些人的排列限制关系(比如x要排在y前面),求符合这些条件的排列总数。 分析: 这题自己没想出来,看了别人的题解。这句“我们想求一堆人的排列总数,它等于在这一堆人中 减去每一个入度为 0 的人,剩下的人的排列总数的和。”就是这题的方法了。记忆化搜索写法。
#include <iostream> #include <memory.h> #include <stdio.h> #include <cstring> using namespace std; #define LL long long
LL dp[(1<<16)+10],g[16][16],din[16],n,m; char name[16][20];
LL find(char s[]) { for(LL i=0;i<=n;i++) { if(strcmp(s,name[i])==0) return i; } return -1; }
LL dfs(LL now) { LL i,j; if(dp[now]>=0) return dp[now]; dp[now]=0; for(i=0;i<n;i++) { if(!din[i]&&(now&(1<<i))) // 如果i的入度为0且now这个状态含有i { for(j=0;j<n;j++) { if(g[i][j]) din[j]--; } dp[now]+=dfs(now-(1<<i)); // 递推关系 for(j=0;j<n;j++) // 恢复 { if(g[i][j]) din[j]++; } } } return dp[now]; }
int main() { LL i,x,y,ans,nmax,j; char s1[20],s2[20]; while(scanf("%lld",&m)!=EOF) { memset(g,0,sizeof(g)); n=-1; for(i=1;i<=m;i++) { scanf("%s %s",s1,s2); x=find(s1); if(x==-1) {strcpy(name[++n],s1); x=n;} y=find(s2); if(y==-1) {strcpy(name[++n],s2); y=n;} g[x][y]=1; } n++; memset(din,0,sizeof(din)); for(i=0;i<n;i++) { for(j=0;j<n;j++) { if(g[i][j]) din[j]++; } } memset(dp,-1,sizeof(dp)); for(i=0;i<n;i++) dp[(1<<i)]=1; // 只剩一个人的时,必然是1 所以先设好边界 nmax=(1<<n)-1; ans=dfs(nmax); printf("%lld\n",ans); } return 0; }
摘要: 题意:给一个三角形,被分成了很多个三角形(具体看题目里的图),求一个最大的只包含白色三角形三角形。分析:不难但有点小恶心的题目。方法应该很多,怎么数都行了。我是用类似dp的方法的,如果上一层的[j+1,k-1]这个范围能构成一个纯白色三角形,并且这一层的[j,k]这个范围也都是白的,那么f[i][j][k]就代表了一个纯白色的大三角形。代码写得有些啰嗦,懒得简化了。
Code hig... 阅读全文
|
|
| 日 | 一 | 二 | 三 | 四 | 五 | 六 |
---|
27 | 28 | 29 | 30 | 31 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|
常用链接
留言簿
随笔分类
随笔档案
搜索
最新评论
阅读排行榜
评论排行榜
|
|