学习遇到瓶颈了。。
o(︶︿︶)o 唉。。
先贴提纲应该进行到的地方
大一假期(如果留校集训)
1. 掌握C++语法,并熟练使用STL //正在学习。。还好
2. 试着实现STL的一些基本容器和函数,使自己基本能看懂STL源码 //还好
3. 图论
a) 使用优先队列优化Dijkstra和Prim //有一点懂
b) 单源最短路径之SPFA //之前实现过
c) 差分约束系统 //完全不懂
d) 多源多点最短路径之FloydWarshall算法 //之前实现过
e) 求欧拉路(圈套圈算法) //完全不懂+1
4. 进行复杂模拟题训练 //额。。还未练习
5. 拓扑排序 //至少未涉及并查集的部分感觉还是比较简单的
6. 动态规划进阶
a) 完全背包、多重背包等各种背包问题(参见背包九讲)//目前只掌握了01,完全和多重。。
b) POJ上完成一定数目的动态规划题目 //以下未进行
c) 状态压缩动态规划
d) 树形动态规划
7. 搜索
a) 回溯法熟练应用
b) 复杂的搜索题目练习
c) 双向广度优先搜索
d) 启发式搜索(包括A*算法,如八数码问题)
8. 计算几何
a) 判断点是否在线段上
b) 判断线段相交
c) 判断矩形是否包含点
d) 判断圆与矩形关系
e) 判断点是否在多边形内
f) 判断点到线段的最近点
g) 计算两个圆的公切线
h) 求矩形的并的面积
i) 求多边形面积
j) 求多边形重心
k) 求凸包
选修
9. 可以学习一种C++的开发框架来编写一些窗体程序玩玩(如MFC,Qt等)。
10. 学习使用C或C++连接数据库。
背包这一块好难啊。。。
自己还没有充分理解背包九讲的内容。。
囧。。
今天上午看了优先队列优化的prim和dijkstra,感觉马马虎虎。。
下午开始的时候看了拓扑排序也还挺简单。。。
找了背包题目的集合来做。。。
01完全和多重完全就是处理一下输入输出,然后套模版。。。
一到其他各种背包就手足无措了。。
关键是没有理解它的含义。。
不能再这样继续盲目做下去。。。
总结反思。。
贴几道今天的成果。。
hdu 1285:http://acm.hdu.edu.cn/showproblem.php?pid=1285
/*
基本的拓扑排序
懂了思想编码就很快了
寻找入度为0的点->该点指向的所有点入度-1
*/
#include<iostream>
using namespace std;
int map[505][505];
int indegree[505];
int ans[505];
int n,m;
void toposort()
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(map[i][j])
{
indegree[j]++;
}
}
}
for(int i=1;i<=n;i++)
{
int j=1;
while(indegree[j]!=0)
{
j++;
}
ans[i]=j;
indegree[j]--;
for(int k=1;k<=n;k++)
{
if(map[j][k])
{
indegree[k]--;
}
}
}
}
int main()
{
while(cin>>n>>m)
{
memset(indegree,0,sizeof(indegree));
memset(map,0,sizeof(map));
while(m--)
{
int x,y;
cin>>x>>y;
map[x][y]=1;
}
toposort();
for(int i=1;i<n;i++)
{
cout<<ans[i]<<" ";
}
cout<<ans[n]<<endl;
}
system("pause");
return 0;
}
hdu 3342: http://acm.hdu.edu.cn/showproblem.php?pid=3342
/*
拓扑排序应用,若一次遍历无indegree=0的点则说明存在环
*/
#include <iostream>
using namespace std;
int map[105][105];
int indegree[105];
int n,m;
void toposort()
{
for(int i=0;i<n;++i)
{
bool flag = false; //用于标识在一次排序中是否有环
for(int j=0;j<n;++j)
{
if(indegree[j]==0)
{
flag=true;
indegree[j]--;
for(int k=0;k<n;++k)
{
if(map[j][k])
indegree[k]--;
}
break;
}
}
if(!flag)
{
cout<<"NO"<<endl;
return ;
}
}
cout<<"YES"<<endl;
}
int main()
{
while(cin>>n>>m&&n!=0)
{
memset(indegree,0,sizeof(indegree));
memset(map,0,sizeof(map));
while(m--)
{
int x,y;
cin>>x>>y;
if(map[x][y]==0)
{
map[x][y]=1;
indegree[y]++;
}
}
toposort();
}
system("pause");
return 0;
}
/*
poj 3624
这题不是hdu上的啊。。
不过一样是模版01背包。。
*/
#include<iostream>
using namespace std;
int f[13000];
int n,m;
void ZeroOnePack(int cost,int weight)//cost 为费用, weight 为价值
{
for(int i=m;i>=cost;i--)
if(f[i-cost]+weight>f[i])
f[i]=f[i-cost]+weight;
}
void CompletePack(int cost,int weight)
{
for(int i=cost;i<=m;i++)
if(f[i-cost]+weight>f[i])
f[i]=f[i-cost]+weight;
}
void MultiplePack(int cost ,int weight,int amount)
{
if(cost*amount>=m)
CompletePack(cost,weight);
else
{
for(int k=1;k<amount;)
{
ZeroOnePack(k*cost,k*weight);
amount-=k;
k<<=1;
}
ZeroOnePack(amount*cost,amount*weight);
}
}
int main()
{
while(cin>>n>>m)
{
for(int i=0;i<n;i++)
{
int cost,weight;
cin>>cost>>weight;
ZeroOnePack(cost,weight);
}
cout<<f[m]<<endl;
}
system("pause");
return 0;
}
/*
poj 3628
理解背包模版中各个量的含义
*/
#include<iostream>
using namespace std;
int f[1000005];
int c[1000005];
int n,m,sum;
void ZeroOnePack(int cost,int weight)//cost 为费用, weight 为价值
{
for(int i=sum;i>=cost;i--)
if(f[i-cost]+weight>f[i])
f[i]=f[i-cost]+weight;
}
int main()
{
while(cin>>n>>m)
{
memset(f,0,sizeof(f));
sum=0;
for(int i=1;i<=n;i++)
{
cin>>c[i];
sum+=c[i];
ZeroOnePack(c[i],c[i]);
}
for(int i=1;i<=sum;i++)
{
// cout<<f[i]<<endl;
if(f[i]>=m)
{
cout<<f[i]-m<<endl;
break;
}
}
}
system("pause");
return 0;
}
/*
想不出。。。
题解见:http://blog.youkuaiyun.com/lyy289065406/article/details/6648094
写的真好。。。
*/
//Memory Time
//1496K 0MS
//我所使用的解题方法,由于dp状态方程组申请空间比较大大
//若dp为局部数组,则会部分机器执行程序时可能由于内存不足会无法响应
//所以推荐定义dp为全局数组,优先分配内存
#include<iostream>
using namespace std;
int dp[21][15001]; //状态数组dp[i][j]
//放入(挂上)前i个物品(钩码)后,达到j状态的方法数
int main(int i,int j,int k)
{
int n; //挂钩数
int g; //钩码数
int c[21]; //挂钩位置
int w[21]; //钩码重量
/*Input*/
cin>>n>>g;
for(i=1;i<=n;i++)
cin>>c[i];
for(i=1;i<=g;i++)
cin>>w[i];
/*Initial*/
memset(dp,0,sizeof(dp)); //达到每个状态的方法数初始化为0
dp[0][7500]=1; //7500为天枰达到平衡状态时的平衡度
//放入前0个物品后,天枰达到平衡状态7500的方法有1个,就是不挂钩码
/*DP*/
for(i=1;i<=g;i++)
for(j=0;j<=15000;j++)
if(dp[i-1][j]) //优化,当放入i-1个物品时状态j已经出现且被统计过方法数,则直接使用统计结果
//否则忽略当前状态j
for(k=1;k<=n;k++)
dp[i][ j+w[i]*c[k] ] += dp[i-1][j]; //状态方程
/*Output*/
cout<<dp[g][7500]<<endl;
return 0;
}
/*
注意递推公式
减法原理应用
其他没什么了
*/
#include<iostream>
#include<cstdio>
using namespace std;
double f[10005];
int n;
int m;
void ZeroOnePack(int cost,double weight)//cost 为费用, weight 为价值
{
for(int i=n;i>=cost;i--)
if(1-(1-f[i-cost])*(1-weight)>f[i])
f[i]=1-(1-f[i-cost])*(1-weight);
}
int main()
{
while(cin>>n>>m,n!=0||m!=0)
{
memset(f,0,sizeof(f));
for(int i=0;i<m;i++)
{
int cost;
double p;
cin>>cost>>p;
ZeroOnePack(cost,p);
}
printf("%.1lf%%\n",f[n]*100);
}
system("pause");
return 0;
}
/*
注意初始化f[0]=1,表示未偷时是安全的,其它初始化为0,表示不安全
*/
#include<iostream>
#include<cstdio>
#include<cmath>
#define MAXN 101
#define MAXV 10001
using namespace std;
int cost[MAXN];
int v,k;
double weight[MAXV],f[MAXV];
void ZeroOnePack(int cost,double weight)//cost 为费用, weight 为价值
{
for(int i=v;i>=cost;i--)
if(f[i-cost]*weight>f[i])
f[i]=f[i-cost]*weight;
}
int main()
{
int T,n,i,j;
double P;
cin>>T;
while(T--)
{
cin>>P>>n;
P=1-P;
v=0;
for(i=0;i<n;i++)
{
cin>>cost[i]>>weight[i];
weight[i]=1-weight[i];
v+=cost[i];
}
memset(f,0,sizeof(f));;
f[0]=1;
for(i=0;i<n;i++)
{
ZeroOnePack(cost[i],weight[i]);
}
bool flag=false;
for(i=v;i>=0;i--)
{
if(f[i]-P>0.000000001)
{
printf("%d\n",i);
break;
}
}
}
return 0;
}
/*
二维01背包
不会
学习
题目描述:给最多40根木棍,每根长度不超过40,
要用完所有的木棍构成面积最大的三角形,
求出最大的面积。
算法核心:二维01背包
使用到海伦公式:
已知三角形的三边长度a,b,c,求面积
S=√[p(p-a)(p-b)(p-c)]
而公式里的p为半周长:
p=(a+b+c)/2
分析:用dp[i][j][k]表示到第i根木棒能否摆出边长分别为j,k的三角形
易得 dp[i][j][k] = dp[i-1][j-x[i]][k]|dp[i-1][j][k-x[i]]|dp[i-1][j][k];
简单的空间压缩,化为二维dp,注意:这里每根木棒只能使用一次,
是01背包,要倒着扫
*/
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
using namespace std;
int x[41],n,tot=0,ans=-1,half;
bool dp[801][801];
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>x[i];
tot+=x[i];
}
memset(dp,0,sizeof(dp));
dp[0][0]=1;
half=tot/2;
for(int i=1;i<=n;i++)
for(int j=half;j>=0;j--)
for(int k=j;k>=0;k--)
if(j>=x[i]&&dp[j-x[i]][k] || k>=x[i]&&dp[j][k-x[i]])
dp[j][k]=1;
for(int i=half;i>=1;i--)
for(int j=i;j>=1;j--)
if(dp[i][j])
{
int k=tot-i-j;
if(i+j>k && i+k>j && j+k>i)
{
double p=(i+j+k)*1.0/2;
int temp=(int)(sqrt(p*(p-i)*(p-j)*(p-k))*100);
if(temp>ans)
ans=temp;
}
}
cout<<ans<<endl;
return 0;
}
/*
分组背包
不会
学习
http://blog.sina.com.cn/s/blog_6ec19c780100w1nn.html
http://blog.youkuaiyun.com/ice2013/article/details/7731539
分组背包问题,大意**要买鞋,有k种牌子,每种牌子至少买一双鞋子。每双鞋子有标价跟实际价值。求用m多的钱买最多价值的鞋。
其实我觉得这题的难点就在处理“至少”这点上面。
状态方程很多都能推出用 dp[k][m] 来表示 已经买了k种鞋 在有m钱状态下的 鞋的最大价值。
状态转移方程为
for( k = 1 ; k <= K ; k++)
{
for( i = 0 ; i < num[k] ; i++)
{
for( j = mm ; j >= m[k][i].m ; j--)
{
if(dp[k][j - m[k][i].m] != -1)
dp[k][j] = Max(dp[k][j] , dp[k][j - m[k][i].m] + m[k][i].v);
if(dp[k-1][j - m[k][i].m] != -1 )
dp[k][j] = Max(dp[k][j] , dp[k-1][j - m[k][i].m] + m[k][i].v);
}
}
}
如果忽略了两个红色的判断句,大家都看得出这只是单纯的01背包且 没有条件限制,加了这两句就能实现至少了。理由如下
一开始我将dp[][]数组初始化为-1表示所有的数都不合法。大于0表示合法
然后将所有的 k = 0 dp[0][]置为0,这是为了 k = 1时能合法计算。
从状态方程中看出,当上一个状态值为-1时表示他不合法。所以当前状态没有计算的必要也不合法答案。
如果计算完第k类商品的取值后,所有的dp[k][]均为-1的时候,第k类表明没有一鞋被买。故所有状态都不合法,接下来的所有值也都将不合法。
在计算第k组商品的过程中,当某个-1变成一个非负数的时候,也就表明当前的第k种已经拿了第i件物品,所以变成合法答案了。
如此推下去,最后一个值dp[k][m],就是答案了。如果依然是-1,就输出impossible把。
*/
#include <math.h>
#include <stdio.h>
#include <string.h>
#include<iostream>
#include <algorithm>
using namespace std;
#define maxn 10005
//#define LOCAL
#define CLR( a , b ) memset( a , b ,sizeof(a) )
int dp[11][maxn] , c[100] , v[100] , N , M , K , Bag[11][101] , Sum[11] ;
int main() {
#ifdef LOCAL
freopen("Input.txt","r",stdin);
freopen("Output.txt","w",stdout);
#endif
int i , j , k , x ;
while( ~scanf( "%d%d%d" , &N , &M , &K) ){
CLR( Sum , 0 ) ;
for( i = 0 ; i < N ; i++ ){
scanf( "%d%d%d" , &x , &c[i] , &v[i] ) ;
Bag[x][ Sum[x] ] = i ; //记录到该品牌
Sum[x] ++ ; //控制个数
}
memset( dp , -1 , sizeof(dp) ) ;
for( i = 0 ; i <= M ; i++ ){ //初始化
dp[0][i] = 0 ;
}
for( i = 1 ; i <= K ; i++ ) //品牌编号
for( j = 0 ; j < Sum[i] ; j++ ) //该编号个数
for( k = M ; k >= c[Bag[i][j]] ; k-- ){
if( dp[i][ k-c[ Bag[i][j] ] ] != -1 )
dp[i][k] = max( dp[i][k] , dp[i][ k-c[Bag[i][j]] ] + v[Bag[i][j] ] ) ;
if( dp[i-1][ k-c[ Bag[i][j]] ] != -1 )
dp[i][k] = max( dp[i][k] , dp[ i-1 ][ k-c[Bag[i][j]] ] + v[Bag[i][j] ] ) ;
}
if( dp[K][M] < 0 )
printf( "Impossible\n" ) ;
else
printf( "%d\n" , dp[K][M] ) ;
}
return 0;
}
/*
分组背包模版
*/
#include<iostream>
#include <algorithm>
using namespace std;
#define MAXN 105
int dp[MAXN];
int map[MAXN][MAXN];
void Group_OneZeroPack(int n,int m)
{
for(int i=1;i<=n;i++ )
for(int j=m;j>=0;j--)
for(int k=1;k<=j;k++ )
dp[j]=max(dp[j],dp[j-k]+map[i][k]);
}
int main()
{
int M , N , i , j , temp ;
while(cin>>n>>m,n!=0&&m!=0)
{
memset(dp,0,sizeof(dp));
for(i=1;i<=n;i++)
for(j=1;j<=m;j++ )
cin>>map[i][j];
Group_OneZeroPack(n,m);
cout<<dp[m]<<endl;
}
return 0;
}
/*
混合背包
像我这种连分组背包都没有掌握的人果然不适合做这题。。
网上搜的题解
http://www.cnblogs.com/ACMan/archive/2012/08/13/2637022.html
*/
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define INF 1<<29
int n, m, sum, g;
int w[101],p[101];
int dp[101][101];
int main()
{
int i, j, x;
while(~scanf("%d%d",&n,&sum))
{
memset(dp,0,sizeof(dp));
for(i=1;i<=n;i++)
{
scanf("%d%d",&m,&g);
for(x=1;x<=m;x++)
scanf("%d%d",&w[x],&p[x]);
if(g==0)
{
for(j=0;j<=sum;j++)//当前组初始化
dp[i][j]=-INF;
for(x=1;x<=m;x++)
for(j=sum;j>=w[x];j--)
dp[i][j]=max(dp[i][j],max(dp[i][j-w[x]]+p[x],dp[i-1][j-w[x]]+p[x]));
}
else if(g==1)
{
for(j=0;j<=sum;j++) //当前组初始化
dp[i][j]=dp[i-1][j];
for(x=1;x<=m;x++)
for(j=sum;j>=w[x];j--)
dp[i][j]=max(dp[i][j],dp[i-1][j-w[x]]+p[x]);
}
else if(g==2)
{
for(j=0;j<=sum;j++) //当前组初始化
dp[i][j]=dp[i-1][j];
for(x=1;x<=m;x++)
for(j=sum;j>=w[x];j--)
dp[i][j]=max(dp[i][j],dp[i][j-w[x]]+p[x]);
}
}
dp[n][sum]=max(dp[n][sum],-1); //没有完成任务的值都为负的,做输出调整,输出-1
printf("%d\n",dp[n][sum]);
}
return 0;
}
加油。