Placing Lampposts - UVa 10859 树形dp

本文探讨了Dhakacity城市照明优化问题,通过构建无环图模型,利用树形动态规划算法,寻找最少路灯数量及最大化双路灯覆盖道路数量的解决方案。

As a part of the mission �Beautification of Dhaka City�, the government has decided to replace all the old lampposts with new expensive ones. Since the new ones are quite expensive and the budget is not up to the requirement, the government has decided to buy the minimum number of lampposts required to light the whole city.

Dhaka city can be modeled as an undirected graph with no cycles, multi-edges or loops. There are several roads and junctions. A lamppost can only be placed on junctions. These lampposts can emit light in all the directions, and that means a lamppost that is placed in a junction will light all the roads leading away from it.

The �Dhaka City Corporation� has given you the road map of Dhaka city. You are hired to find the minimum number of lampposts that will be required to light the whole city. These lampposts can then be placed on the required junctions to provide the service. There could be many combinations of placing these lampposts that will cover all the roads. In that case, you have to place them in such a way that the number of roads receiving light from two lampposts is maximized.

Input

There will be several cases in the input file. The first line of input will contain an integer T(T<=30) that will determine the number of test cases. Each case will start with two integers N(N<=1000) and M( M<N) that will indicate the number of junctions and roads respectively. The junctions are numbered from 0 to N-1. Each of the next M lines will contain two integers a and b, which implies there is a road from junction a to b,
( 0<= a,b < N ) and a != b. There is a blank line separating two consecutive input sets.

Output

For each line of input, there will be one line of output. Each output line will contain 3 integers, with one space separating two consecutive numbers. The first of these integers will indicate the minimum number of lampposts required to light the whole city. The second integer will be the number of roads that are receiving lights from two lampposts and the third integer will be the number of roads that are receiving light from only one lamppost.

Sample Input

2
4 3
0 1
1 2
2 3

5 4
0 1
0 2
0 3
0 4

Sample Output

2 1 2
1 0 4

题意:有n个点和m条街,每个点上可以装一个路灯,每条街都必须在两端至少有一个灯,问至少需要多少个灯,如果灯数相同的情况下,最多有多少条街可以在两端都有路灯。

思路:简单的树形dp,就是本题可能不止是一棵树。dp[u][0 1][k]表示在u节点不安装/安装路灯的情况,dp[u][k][0 1]表示u节点及其以下的街都满足要求是最少的路灯数和最大的两端都有灯的街数。

AC代码如下:

#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
int n,m,dp[1010][2][2],vis[1010];
vector<int> vc[1010];
void dfs(int u,int f)
{
    vis[u]=1;
    dp[u][1][0]=1;
    int i,j,k,len=vc[u].size(),v;
    for(i=0;i<len;i++)
    {
        v=vc[u][i];
        if(v==f)
          continue;
        dfs(v,u);
        dp[u][0][0]+=dp[v][1][0];
        dp[u][0][1]+=dp[v][1][1];

        if(dp[v][0][0]<dp[v][1][0])
        {
            dp[u][1][0]+=dp[v][0][0];
            dp[u][1][1]+=dp[v][0][1];
        }
        else if(dp[v][0][0]>dp[v][1][0])
        {
            dp[u][1][0]+=dp[v][1][0];
            dp[u][1][1]+=dp[v][1][1]+1;
        }
        else
        {
            dp[u][1][0]+=dp[v][0][0];
            dp[u][1][1]+=max(dp[v][0][1],dp[v][1][1]+1);
        }
    }
}
int main()
{
    int T,t,i,j,k,u,v,a,b,c;
    scanf("%d",&T);
    for(t=1;t<=T;t++)
    {
        scanf("%d%d",&n,&m);
        for(i=0;i<n;i++)
           vc[i].clear();
        for(i=1;i<=m;i++)
        {
            scanf("%d%d",&u,&v);
            vc[u].push_back(v);
            vc[v].push_back(u);
        }
        memset(dp,0,sizeof(dp));
        memset(vis,0,sizeof(vis));
        a=0;b=0;c=0;
        for(i=0;i<n;i++)
           if(vis[i]==0)
           {
               dfs(i,-1);
               if(dp[i][0][0]<dp[i][1][0])
                 a+=dp[i][0][0],b+=dp[i][0][1];
               else if(dp[i][0][0]>dp[i][1][0])
                 a+=dp[i][1][0],b+=dp[i][1][1];
               else
                 a+=dp[i][0][0],b+=max(dp[i][0][1],dp[i][1][1]);
            }
        printf("%d %d %d\n",a,b,m-b);
    }
}



<think>我们正在讨论的是在Android设备的恢复模式(如TWRP)中使用的shell脚本,因为`ui_print`是常用于此类环境中的命令。在标准的Linuxshell中并没有`ui_print`命令,它通常是由自定义恢复环境提供的。因此,在编写用于恢复刷机的脚本时,我们会使用`ui_print`来在恢复屏幕上显示信息。下面将分别说明这三个命令在恢复脚本中的使用:1.**ui_print**:用于在恢复模式下向用户显示消息。它类似于普通shell脚本中的`echo`,但专门针对恢复模式的输出。在恢复脚本中,我们通常这样使用:```shellui_print"正在安装更新..."```2.**mkdir**:创建目录。在恢复脚本中,我们可能需要创建目录来放置文件。注意,恢复模式下的文件系统可能和正常启动的系统不同,所以需要确保目标分区已经挂载。```shellmkdir-p/system/new_folder```其中`-p`选项允许创建多级目录,并且如果目录已存在也不会报错。3.**cp**:复制文件或目录。在恢复脚本中,我们经常需要复制文件到系统分区。```shellcp/sdcard/update_file/system/app/```注意:在恢复脚本中,我们通常需要先挂载系统分区为可写,否则无法复制文件到系统分区。下面是一个综合示例,假设我们是在一个TWRP恢复脚本中,需要显示一些信息,创建目录并复制文件:```shell#!/sbin/sh#显示开始信息ui_print"开始安装自定义模块..."#挂载系统分区为可读写mount/system#在/system目录下创建一个新的目录ui_print"创建目录..."mkdir-p/system/app/MyApp#复制文件ui_print"复制文件..."cp/sdcard/MyApp.apk/system/app/MyApp/#设置权限(如果需要)ui_print"设置权限..."chmod644/system/app/MyApp/MyApp.apk#卸载系统分区(可选)umount/systemui_print"安装完成!"```注意事项:-恢复脚本通常以`#!/sbin/sh`开头,因为恢复环境中的shell路径通常是`/sbin/sh`。-在操作`/system`分区之前,必须先挂载它。不同的设备可能分区路径不同,所以有时可能需要使用`/system_root`等,具体取决于设备。-在恢复脚本中,我们通常需要设置文件的权限,因为从sdcard复制的文件可能权限不正确。-在脚本结束时,可以选择卸载分区,但TWRP通常会在刷机完成后自动卸载。另外,如果是在普通的Linuxshell脚本(非恢复环境)中使用,那么`ui_print`将不可用,此时应使用`echo`代替。例如:```shellecho"正在创建目录..."mkdir-p/path/to/direcho"复制文件..."cpsourcedestination```§§相关问题§§1.在TWRP恢复脚本中如何挂载和卸载分区?2.在恢复脚本中如何设置文件的权限?3.恢复脚本中常见的错误有哪些?如何调试?4.如何制作一个可刷入的ZIP包(包含脚本和文件)?</think>### 在Shell脚本中使用ui_print、mkdir和cp命令 在Shell脚本中,`ui_print`、`mkdir`和`cp`是常用的命令组合,特别是在Android恢复脚本或刷机包中。以下是详细说明和示例: #### 1. **ui_print命令** - **作用**:在恢复模式(如TWRP/CWM)中显示进度信息到用户界面 - **语法**:`ui_print "消息内容"` - **注意**: - 仅适用于Android恢复环境 - 普通Linux终端需替换为`echo` - **示例**: ```shell ui_print "开始安装程序..." ui_print "版本: 1.0" ``` #### 2. **mkdir命令** - **作用**:创建目录 - **常用参数**: - `-p`:递归创建多级目录(目录不存在时自动创建父目录) - `-m`:设置目录权限(如`-m 755`) - **示例**: ```shell mkdir /system/app/MyApp # 创建单级目录 mkdir -p /system/priv-app/MyApp # 递归创建目录 mkdir -m 755 /data/tmp # 创建带权限的目录 ``` #### 3. **cp命令** - **作用**:复制文件/目录 - **常用参数**: - `-r`:递归复制目录 - `-f`:强制覆盖 - `-p`:保留文件属性(权限/时间戳) - **示例**: ```shell cp /sdcard/update.zip /cache/ # 复制文件 cp -rf /tmp/modules/* /system/lib/ # 递归复制目录 cp -p script.sh /system/bin/ # 保留属性复制 ``` --- ### 完整脚本示例 ```shell #!/sbin/sh # 显示安装进度 ui_print "[1/4] 正在创建目录..." mkdir -p /system/app/MyApp mkdir -p /system/priv-app/MyAppExtra ui_print "[2/4] 复制应用文件..." cp -f /sdcard/MyApp.apk /system/app/MyApp/ cp -rf /sdcard/lib/* /system/lib/ ui_print "[3/4] 设置权限..." chmod 644 /system/app/MyApp/MyApp.apk chmod 755 /system/lib/*.so ui_print "[4/4] 安装完成!" ``` --- ### 关键注意事项 1. **环境差异**: - `ui_print`仅在Android恢复环境有效,普通Shell脚本用`echo` ```shell # 通用写法 [ -n "$RECOVERY_OUT" ] && ui_print "消息" || echo "消息" ``` 2. **权限问题**: - 系统目录操作需先挂载分区: ```shell mount /system # 挂载system分区 mkdir /system/new_folder umount /system # 操作后卸载 ``` 3. **错误处理**: ```shell if ! cp file /destination; then ui_print "错误: 文件复制失败!" exit 1 fi ``` 4. **路径规范**: - 使用绝对路径避免歧义 - 特殊字符需转义:`cp "文件 名.txt" /dest` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值