DP----cf#336 div2 C D

来源:互联网 发布:抑郁症测试 知乎 编辑:程序博客网 时间:2024/06/06 09:08

http://codeforces.com/contest/608/problem/D

2016-5-9 思路:
C,对于题目先不去想算法:暴力的想法 就是n*n,对于每一个灯塔我都模拟一遍看有多少个被炸掉的T(即多少个被激活),选最小的炸掉的即选最多被激活的!
不难有一种想法: 先设一个数组全部初始化为0,for(int i=n;i>=1;i–) ,对于每一个灯塔,我们记录从后面激活到这个地方炸掉的最少的灯塔num1,然后算激活它炸掉了多少灯塔num2,然后更新他爆炸范围外的第一个灯塔,表示这个灯塔!可能!的最小值是Num1+num2, 即dp[j]=min(dp[j],num1+num2)。 然后一直更新到dp[1] , 就可以得到最少炸掉的灯塔数。 具体实现我没管,我觉得这种想法必然可行,理论AC。 看看题目,发现有个条件没用: 在右边可以选择一个任意位置任意爆炸的T激活(潜台词就是:可以选任意的灯塔开始激活) 所以我们初始化不能为0,而应该对于i, 初始为 (i+1—n 的灯塔都炸掉) ,好了,理论ac完成

D:
题意: 对一个串,每次可摧毁回文串,最少多少步能摧毁整个串,就是祖玛游戏。
有点难 ,哈哈
区间dp;

不会做不会做,等我dp变强再回过头来想吧

一个人在实验室做的这场比赛,被前两题的小错误卡了很久,水了两道题就滚了。
C:
就是说有n个塔,每个塔有一个爆炸范围power,从右到左激活灯塔,若激活一个灯塔则他会把它power范围内的灯塔都炸掉,再激活下一个灯塔。 可以在右边插入一个任何位置任何power的灯塔,求最小的被炸掉的灯塔数。
可以设置dp[i] 代表如果激活灯塔i,则摧毁dp[i]个灯塔。
如此可以知道状态转移方程:
用二分在序列中找到 >=pos[i]-power[i] 的第一个 灯塔=ppos。
dp[i]=dp[ppos-1] + (i-ppos) //之前的一个爆炸数+ i炸掉的灯塔数

//省略头文件pair<int,int> p[1000005];int dp[1000005];int main(){    int n;    scanf("%d",&n);    for (int i=0;i<n;i++)        scanf("%d %d",&p[i].first,&p[i].second);    sort(p,p+n);  //排序,为了后一步的dp二分    int ans;    for (int i=0;i<n;i++){        int pp=lower_bound(p,p+n,make_pair(p[i].first-p[i].second,-1))-p;        //找到爆炸之外的第一个灯塔        dp[i]=dp[pp-1]+i-pp;  //i-pp则为激活i炸掉的灯塔数        ans=min(ans,n-i-1+dp[i]);    }    cout<<ans;}

D题 ——-也是一个dp
一开始在想一维的dp , 设置了两个dp[n]。 然而脑容量不够,并没有想出什么好的东西。
想对于i 从0-i中找一个地址pos,形成回文 所以dp[i]=dp[pos-1]+1;
然而对于第三组样例 会出现 144 232 1 “232”夹在中间导致 dp[n-1] 无法与pos=0形成回文。就开始想着条件应该是 num[i]==num[pos] 就可以开始进行dp状态转移,但是当时思维局限在一维上面了,想半天并没有什么卵用。 最后还是去看了下别人的代码,看到别人用二维便有了些了解 。

dp[i][j] 代表 i到j范围内 最小的步数
如果num[i]==num[j]
则比较容易想到: dp[l][r] =dp[l+1][k-1]+dp[k+1][r]
于是 应该想到:先要从dp[i][i] 到dp[i][i+1] 到dp[i][i+n-1] 依次从左到右的更新

所以一开始应该更新
for(int i=0;i< n;i++)
dp[i][i]=1;
然后更新0-1, 1-2,2-3,3-4,…
0-2,1-3, 2-4,3-5…..
0-3, 1-4,2-5…..
0-n-1
为什么这样呢?因为可以让初始化dp[i][j]=dp[i+1][j]+1;
然后进行状态转移。

int num[505];int dp[505][505];int main(){    freopen("1.txt","r",stdin);    int n;    while(~scanf("%d",&n)){         for(int i=0;i<n;i++)             scanf("%d",&num[i]);         //mem(dp);         int l,r;         for(int i=0;i<n;i++)             dp[i][i]=1;         //从0 开始  达到n  ——循环n次,         //从0-1 1-2 2-3 3-4 ...          //  0-2 1-3 2-5...          //  0-n-1         for(int i=0;i<n;i++)             for(int j=0;j<n-i;j++){    //j<n-j 是保证 r<n                 l=j;                 r=i+j;                 if (l==r)                     continue;                 dp[l][r]=dp[l+1][r]+1;   //若是改成dp[l][r]=dp[l][r-1]+1;会错,但是具体错在哪里我也还没有搞清楚                 for(int k=l+1;k<=r;k++){                     if(num[l]==num[k]){                          if(k==l+1)                              dp[l][r]=min(dp[l][r],1+dp[k+1][r]);                          else                              dp[l][r]=min(dp[l][r],dp[l+1][k-1]+dp[k+1][r]);                     }                 }            }         printf("%d\n",dp[0][n-1]);    }    return 0;}
0 0
原创粉丝点击