5-15
题目大意:一个人的房子着火了,里面有n件物品,第i件物品的价值是pi,抢救它需要ti时间,而在di时间这件物品会被烧完,无法抢救,问救出的最大价值是多少,并输出任意的救援方案。
跟暑假留校选拔的一题类似,就是01背包保存路径,那我们直接可以把dp弄成个结构体,记录权值,以及vector保存相应的物品。
具体思路就是,价值已知,而时间可以看做体积,最大体积无非就是最大的d,2000。不过多了个p和d的限制,我们每个物品可以更新的区域就在[t,d-1]之间,在01背包更新时,我们是由前面的最优来决定现在的最优,如果前面都不是最优的那么当前肯定并不是最优。比如当前时间是j选择救第i件物品,得到的最大价值就是dp[j]=max(dp[j],dp[j-t[i]]+p[i]),而如果dp[j-t[i]]都不是最大价值,那么dp[j]肯定就不是最大价值。什么时候会出现这种情况,就是对dp[j]进行更新时,dp[j-t[i]]还没更新的时候。那怎么让dp[j-t[i]]先更新,由每个物品的更新区域我们可以看出,右边界是由d决定的,所以要想dp[j-t[i]]先更新,也就先用d小的物品进行更新。
1 #include<cstdio> 2 #include<vector> 3 #include<algorithm> 4 using namespace std; 5 struct Node{ 6 int val; 7 vector<int> wp; 8 Node(){ 9 val=0; 10 wp.clear();//wp就记录下相应救下的是哪些物品 11 } 12 }dp[2118],ans; 13 struct Wp{ 14 int t,d,p,id; 15 bool operator<(const Wp &w1)const{ 16 return d<w1.d;//按烧毁时间早的排 17 } 18 }w[118]; 19 int main() 20 { 21 int n; 22 scanf("%d",&n); 23 for(int i=1;i<=n;i++) 24 { 25 w[i].id=i; 26 scanf("%d%d%d",&w[i].t,&w[i].d,&w[i].p); 27 } 28 sort(w+1,w+1+n); 29 for(int i=1;i<=n;i++) 30 { 31 for(int j=w[i].d-1;j>=w[i].t;j--) 32 { 33 if(dp[j].val<dp[j-w[i].t].val+w[i].p)//01背包的转移过程 34 { 35 dp[j].val=dp[j-w[i].t].val+w[i].p; 36 dp[j].wp=dp[j-w[i].t].wp; 37 dp[j].wp.push_back(w[i].id); 38 } 39 } 40 } 41 for(int i=0;i<=2000;i++) 42 if(ans.val<dp[i].val) 43 ans=dp[i]; 44 printf("%d\n%d\n",ans.val,ans.wp.size()); 45 for(int i=0;i<ans.wp.size();i++) 46 { 47 if(i) 48 putchar(' '); 49 printf("%d",ans.wp[i]); 50 } 51 return 0; 52 } 53 /* 54 2 55 1 3 2 56 1 2 2 57 */
不在dp里套用vector来记录路径的话,也可以记录下每个时间抢救过那些物品以及最后抢救的物品,然后进行回溯。
1 #include<cstdio> 2 #include<vector> 3 #include<algorithm> 4 using namespace std; 5 struct Wp{ 6 int t,d,p,id; 7 bool operator<(const Wp &w1)const{ 8 return d<w1.d;//按烧毁时间早的排 9 } 10 }w[118]; 11 int dp[2118],wp[2118],vis[118][2118]; 12 void dfs(int num,int i,int j) 13 { 14 if(!j) 15 { 16 printf("%d\n",num); 17 return ; 18 } 19 if(!vis[i][j])//如果i这个物品没在j时间更新过答案,就看上一个物品 20 dfs(num,i-1,j); 21 else 22 { 23 dfs(num+1,i-1,j-w[i].t);//否则减去i这个物品耗费的时间 24 printf("%d",w[i].id); 25 if(i) 26 putchar(' '); 27 } 28 } 29 int main() 30 { 31 int n; 32 scanf("%d",&n); 33 for(int i=1;i<=n;i++) 34 { 35 w[i].id=i; 36 scanf("%d%d%d",&w[i].t,&w[i].d,&w[i].p); 37 } 38 sort(w+1,w+1+n); 39 for(int i=1;i<=n;i++) 40 { 41 for(int j=w[i].d-1;j>=w[i].t;j--) 42 { 43 if(dp[j]<dp[j-w[i].t]+w[i].p)//01背包的转移过程 44 { 45 dp[j]=dp[j-w[i].t]+w[i].p; 46 vis[i][j]=1;//标记下j时间使用过i物品来更新 47 wp[j]=i; 48 } 49 } 50 } 51 int ans=0,j=0; 52 for(int i=0;i<=2000;i++) 53 if(ans<dp[i]) 54 { 55 ans=dp[i]; 56 j=i; 57 } 58 printf("%d\n",ans); 59 dfs(0,wp[j],j); 60 return 0; 61 } 62 /* 63 2 64 1 3 2 65 1 2 2 66 */
CodeForces - 894CMarco and GCD Sequence
题目大意:给你一个序列的所有子区间的gcd的set集合,问你能不能反过来构造出这个序列,不能的话输出-1,能的话输出构造的序列
嗯,一开始就觉得他给出的S序列就是答案,因为当l==r时的[l,r]子区间的gcd就是相应的数本身,所以只要判断所有数的gcd是不是最小的s0就好,因为最小那个数肯定是所有数的gcd。但这样有个问题,比如给出的序列是1 3 10 15,那么它们的gcd就应该还有个5,而不是单单这个序列。那怎么办呢,所有数肯定是最小的s0的倍数,那么每个数和s0的gcd肯定是s0,那么我们在每个数的前后都加上一个s0的话,这样对于每个数,除了l==r时的它的gcd为本身外,其他区间的gcd都等于s0,而不会产生新的gcd。
1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 int a[1108]; 5 int main() 6 { 7 int n; 8 scanf("%d",&n); 9 for(int i=0;i<n;i++) 10 scanf("%d",&a[i]); 11 int g=0; 12 for(int i=0;i<n;i++) 13 g=__gcd(g,a[i]); 14 if(g!=a[0]) 15 printf("-1\n"); 16 else 17 { 18 printf("%d\n",2*n+1); 19 printf("%d",a[0]); 20 for(int i=0;i<n;i++) 21 printf(" %d %d",a[i],a[0]); 22 } 23 return 0; 24 }
题目大意:I=1, V=5, X=10, L=50,对于一个字母序列,它的权值是所有字母权值之和,比如IXX的权值就是1+10+10=21,给你序列的长度,问组合多少个不同的权值。
没啥想法,就打表看了下,发现从长度是11后,每次长度+1,答案就+49,打表程序为注释部分。
1 #include<cstdio> 2 #include<cstring> 3 #include<cmath> 4 using namespace std; 5 typedef long long ll; 6 const int a[4]={1,5,10,50}; 7 const ll ans[15]={1,4,10,20,35,56,83,116,155,198,244,292,341}; 8 //int n,ans,vis[1000000]; 9 //void dfs(int x,int sum) 10 //{ 11 // if(x==n) 12 // { 13 // if(!vis[sum]) 14 // { 15 // ans++; 16 // vis[sum]=1; 17 // } 18 // return ; 19 // } 20 // for(int i=0;i<4;i++) 21 // dfs(x+1,sum+a[i]); 22 //} 23 int main() 24 { 25 int n; 26 while(~scanf("%d",&n)) 27 { 28 // ans=0; 29 // memset(vis,0,sizeof(vis)); 30 // dfs(0,0); 31 // printf("%d %d\n",ans,ans-ans2); 32 // ans2=ans; 33 if(n<=12) 34 printf("%lld\n",ans[n]); 35 else 36 printf("%lld\n",ans[12]+1ll*(n-12)*49ll); 37 } 38 return 0; 39 }