每日一题(11): poj1276

来源:互联网 发布:ubuntu 访问网络命令 编辑:程序博客网 时间:2024/06/14 13:23
1. 本来觉得很简单,却耗费了一天半的时间。本来做完1837之后感觉已经明白了怎么做。但是事实仿佛并非如此。当然,这也有自己的一点执念。

2. 这几天体会到,写程序就像做数学题一样,很忌讳一算就错。程序也是,不能想起来觉得这样这样,但是写起来没有把握对。就像前两天有个演讲说的那样,印度的程序很厉害,因为所有人写的都一样。程序是工业产品,流水线上的产品,是有标准答案的。

3. 总觉得不要怕。自己在这次中遇到了很严重的问题,不知道怎么错的。是自己的思路。要是以往,也许就是看看网上别人的思路,照着别人的思路写。这样,即使写不对还可以有答案看。但是这次自己逼着自己去一点一点思考,到底哪里有问题。程序就是这样,肯定是思路的不严谨。要勇敢,敢于啃硬骨头。一定要想明白。这对于上面的自信很重要。一定要将来,自己写的代码有把握不出错。

4. 说这道题,其实就是多重背包问题。背包九讲里面说的很好。就不多说了。只是我写了好几个版本,包括原始的,暴力,二进制优化的。

5.自己的版本一直过不了。因为超时。原来是因为有问题,自己思路错了。

6. 这道题是多个测试案例。不要忘了。否则就是wa。但是题目里面没有

下面是各个版本的代码:

7. 首先是自己的思路,很基本的三层循环:

 #include<iostream>#include<stdio.h>#include<string.h>using namespace std;int n;int c;int num[20];int cash[20];int dp[11][100005];int main(){//freopen("input.txt","r",stdin);while(~scanf("%d%d",&c,&n)){//cin>>c>>n;//这里其实可以不要。只是为了剪枝。for(int i = 1; i<=n; i++){cin>>num[i]>>cash[i];}if(n==0||c==0){cout <<0<<endl;continue;}memset(dp,0,sizeof(dp));for(int i = 1; i<=n; i++){for(int j = cash[i]; j<=c; j++){int temp=0;int flag = 0;for(int k = 0; k<=num[i]; k++){//在此处错了好久。因为temp是要取最大的。所以要加if来判断。if(j>=k*cash[i]){if(temp<dp[i-1][j-k*cash[i]] + k*cash[i])temp = dp[i-1][j-k*cash[i]] + k*cash[i];//flag=1;//break;}}//原来把这写到里面了。而且k没有去0,这样就麻烦了。if(temp>dp[i-1][j])dp[i][j] = temp;else dp[i][j] =dp[i-1][j];}}cout <<dp[n][c]<<endl;}}

8, 上面的代码进行空间优化:还是超时,测试案例都可以过,就是超时。根据和网上大家思路的比对,自己确实是最慢的。而且也没有想到怎么剪枝。

#include<iostream>#include<stdio.h>#include<string.h>using namespace std;int n;int c;int num[20];int cash[20];int dp[100005];int main(){//freopen("input.txt","r",stdin);//没有while是肯定错的。while(~scanf("%d%d",&c,&n)){//scanf("%d%d",&c,&n);for(int i = 1; i<=n; i++){cin>>num[i]>>cash[i];}//cout<<"test: "<<c<<","<<n<<endl;//cin>>c>>n;if(c==0){cout << 0<<endl;continue;}if(n==0){cout <<0<<endl;continue;}memset(dp,0,sizeof(dp));for(int i = 1; i<=n; i++){for(int j = c; j>=1; j--){int temp=0;//int flag = 0;for(int k = 0; k<=num[i]; k++){if(j>=k*cash[i]){if(temp<dp[j-k*cash[i]] + k*cash[i])temp = dp[j-k*cash[i]] + k*cash[i];//flag=1;//break;}//if(j+k*cash[i]>c) break;}if(temp>dp[j])dp[j] = temp;//cout<<"test:"<<dp[j]<<endl;//else dp[j] =dp[j];}}cout <<dp[c]<<endl;}}
9. 在网上看到的暴力解法:

#include<iostream>#include<stdio.h>#include<string.h>using namespace std;int n;int c;int num[20];int cash[20];int dp[100005];int main(){//freopen("input.txt","r",stdin);while(~scanf("%d%d",&c,&n)){for(int i = 1; i<=n; i++){cin>>num[i]>>cash[i];}//cout<<"test: "<<c<<","<<n<<endl;//cin>>c>>n;if(c==0){cout << 0<<endl;continue;}if(n==0){cout <<0<<endl;continue;}memset(dp,0,sizeof(dp));int maxn = 0;dp[0] = 1;for(int i =1; i<=n;i++){//for(int j = maxn; j<=c; j++)//因为其他都是0//这里是可以用maxn的,其实其他的方法也可以。只是没有maxn,这里正好求出来了每次的最大值。另外,从现在的全部区间,求更大区间,应该可以理解。for(int j = maxn; j>=0; j--){if(dp[j]==1){for(int k = 1; k<=num[i]; k++){int temp = j+k*cash[i];if(temp>c)continue;dp[temp] = 1;if(temp>maxn)maxn = temp;}}}}cout <<maxn<<endl;}}

10. 多重背包的标准写法:

#include<iostream>#include<stdio.h>#include<string.h>using namespace std;int dp[100005];int cash[150];//int num[20];int c,n;int count;int main(){freopen("input.txt","r",stdin);while(~scanf("%d%d",&c,&n)){memset(dp,0,sizeof(dp));int numTemp,cashTemp;count = 1;for(int i = 1; i<=n; i++){cin>>numTemp>>cashTemp;if(numTemp==0) continue;cash[count] = cashTemp;count++;if(numTemp-1>0){int countTemp = numTemp-1;int k = 2;while(countTemp>0){countTemp = countTemp-k;//不能把这里的代码去掉。因为如果去掉了,总数会少if(countTemp<0){cash[count++] = cashTemp*(countTemp+k);break;}cash[count++] = cashTemp * k;k = k*2;}}}for(int i = 1; i <count; i++){for(int j = c; j >= 0; j--){if(j>=cash[i])if(dp[j-cash[i]]+cash[i]>dp[j])dp[j]=dp[j-cash[i]]+cash[i];}}cout<<dp[c]<<endl;}}

11. 这里总结一下背包问题的几个点,便于以后写。那就是首选空间优化过的,时间上优化过的。也就是最好的算。完全和01在第二层循环上不同。多重在输入上不同。还有,就是第一层循环的起始和终点一般没有可优化的地方。但是二层,可以。一般起始或者终点可以优化。而且,要注意顺序。还有就是dp[j]是等于或者加等于起始都可以。因为每个点都知访问一次。

12. 如果要记录路径的话,应该就可最大序列差不多了。

0 0
原创粉丝点击